说在前面
还以为是什么神仙题...然后读了一下题发现很可做,但是有点卡。编号从0开始没注意,TLE+MLE一堆
题意简述
给一个\(n\)个节点的带权树和一个正整数\(k\),求一个简单路径,使得边权和等于\(k\),同时边数最少,输出最小边数。数据范围见题。
简单口胡
一看见题目就知道是点分治,然后考虑在点分治的基础上来维护最小值,发现在更新答案的时候,正常的点分治都是在前面的子树中找是否有能与它匹配起来和为\(k\)的。那么我们现在就改成:和为\(k\)中边数最小的。用\(Min[d]\)代表边权和为\(d\)时当前的最短边个数。初始化除\(Min[0] = 0\)其余均为\(+\infty\)。分别求出每个点到当前root的边权和和深度。
对于更新答案:
\(ans = \min(ans,dep[i] + Min[k - dis[i]])\)
对于更新\(Min\):
\(Min[dis[i]] = \min(Min[dis[i]],dep[i])\)
同时在搜索时加上必要剪枝。
# include <bits/stdc++.h>
using namespace std;
# define int long long
const int N = 2e5 + 5;
const int K = 1e6 + 5,inf = 1e10;
int n,k;
struct edge
{
int v,w;
edge() {}
edge(int _v,int _w) : v(_v),w(_w) {}
};
vector <edge> g[N];
int siz[N],max_siz[N];
int Min[K];
// int Minn[K];
bool del[N];
int dep[N],dis[N];
int root = 0,S;
int cnt = 0;
int tmp[N];
int ans;
void find_root(int x,int fa)
{
siz[x] = 1,max_siz[x] = 0;
for(int i = 0; i < (int)g[x].size(); i++)
{
int v = g[x][i].v;
if(v == fa || del[v]) continue;
find_root(v,x);
siz[x] += siz[v];
if(siz[v] > max_siz[x]) max_siz[x] = siz[v];
}
max_siz[x] = max(max_siz[x],S - max_siz[x]);
if(max_siz[x] < max_siz[root]) root = x;
return;
}
void Get_dis(int x,int fa)
{
if(dis[x] <= k && dep[x] <= ans) tmp[++cnt] = x;
if(dep[x] >= ans) return;
for(int i = 0; i < (int)g[x].size(); i++)
{
int v = g[x][i].v;
if(v == fa || del[v]) continue;
dis[v] = dis[x] + g[x][i].w;
dep[v] = dep[x] + 1;
Get_dis(v,x);
}
return;
}
void solve(int x)
{
queue <int> q;
for(int i = 0; i < (int)g[x].size(); i++)
{
int v = g[x][i].v;
if(del[v]) continue;
dis[v] = g[x][i].w;
dep[v] = 1;
cnt = 0;
Get_dis(v,x);
for(int j = 1; j <= cnt; j++)
{
ans = min(ans,dep[tmp[j]] + Min[k - dis[tmp[j]]]);
}
for(int j = 1; j <= cnt; j++)
{
Min[dis[tmp[j]]] = min(Min[dis[tmp[j]]],dep[tmp[j]]); // Min[dis] = dep
q.push(tmp[j]);
}
}
while(!q.empty())
{
int top = q.front();
Min[dis[top]] = inf;
Min[k - dep[top]] = inf;
q.pop();
}
return;
}
void dfs(int x)
{
// printf("dfs : x = %lld\n",x);
del[x] = 1;
Min[0] = 0;
dep[x] = 0;
solve(x);
// printf("YES:solve\n");
// for(int i = 1; i <= k; i++) Min[i] = inf;
for(int i = 0; i < (int)g[x].size(); i++)
{
int v = g[x][i].v;
if(del[v]) continue;
max_siz[root = 0] = S = siz[v];
find_root(v,0);
find_root(root,0);
dfs(root);
}
return;
}
signed main(void)
{
scanf("%lld%lld",&n,&k);
for(int i = 1; i < n; i++)
{
int u,v,w;
scanf("%lld%lld%lld",&u,&v,&w);
++u,++v;
g[u].push_back(edge(v,w));
g[v].push_back(edge(u,w));
}
max_siz[root = 0] = S = n;
find_root(1,0);
find_root(root,0);
ans = inf * 10;
for(int i = 1; i <= k; i++) Min[i] = inf;
dfs(root);
printf("%lld\n",ans <= n ? ans : -1);
return 0;
}