思路
- 点分治的整个过程其实就是先找到一个点,把经过他的所有路径(必定dfs)搞完,如果有重复用一下两种形式去重,然后继续分治每一个子树。
- 点分治的calc函数有两种形式:一种是把先算整个树的贡献,再减去每个子树内不合法的。我们发现这类问题都有可减性(求和),可以容斥来解;第二种无法容斥是就是一个一个子树的计算,计算完一个子树之后,再用他来更新。点分治的calc函数有两种形式:一种是把先算整个树的贡献,再减去每个子树内不合法的。我们发现这类问题都有可减性(求和),可以容斥来解;第二种无法容斥是就是一个一个子树的计算,计算完一个子树之后,再用他来更新。
- 此题为第二类。对于一个分治重心u,我们顺序计算每一个子树的贡献,记录d[i]为之前子树到u的长度为 i 的最小边数。计算时只要一边dfs,一边
ans=min(ans,d[k−dis[u]]+c[u])及它的边数和他的另一半的边数之和
- 然后我们要用这颗子树更新d[]值,一边dfs,一边更新即可
d[dis[u]]=min(d[dis[u],c[u])
- 我们发现现在的d[]值只是针对当前的分治重心u的,所以我们还需要一个dfs,去恢复d[]值,恢复到inf;
- 要注意:d一定要赋初值,inf
code
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cmath>
#include<cstdlib>
#include<ctime>
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
inline int read(){
char ch=' ';int f=1;int x=0;
while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
return x*f;
}
const int N=200100;
const int M=1000100;
struct node
{
int v,nxt,w;
}edge[N<<1];
int head[N],cnt;
void add(int u,int v,int w)
{
cnt++;
edge[cnt].v=v;
edge[cnt].w=w;
edge[cnt].nxt=head[u];
head[u]=cnt;
}
int sim[N],mxson[N];
int MX,root,smer;
int dis[N],c[N],d[M];
int k,ans;
bool vis[N];
void getroot(int u,int fa)
{
sim[u]=1;mxson[u]=0;
for(int i=head[u];i;i=edge[i].nxt)
{
int v=edge[i].v;
if(vis[v]||v==fa) continue;
getroot(v,u);
sim[u]+=sim[v];
mxson[u]=max(mxson[u],sim[v]);
}
mxson[u]=max(mxson[u],smer-sim[u]);
if(mxson[u]<MX)
{
MX=mxson[u];root=u;
}
}
void dfs(int u,int fa)
{
if(dis[u]<=k) ans=min(ans,d[k-dis[u]]+c[u]);
for(int i=head[u];i;i=edge[i].nxt)
{
int v=edge[i].v;
int w=edge[i].w;
if(vis[v]||v==fa) continue;
dis[v]=dis[u]+w;c[v]=c[u]+1;
dfs(v,u);
}
}
void add(int u,int fa)
{
if(dis[u]<=k) d[dis[u]]=min(d[dis[u]],c[u]);
for(int i=head[u];i;i=edge[i].nxt)
{
int v=edge[i].v;
if(vis[v]||v==fa) continue;
add(v,u);
}
}
void recover(int u,int fa)
{
if(dis[u]<=k) d[dis[u]]=inf;
for(int i=head[u];i;i=edge[i].nxt)
{
int v=edge[i].v;
if(vis[v]||v==fa) continue;
recover(v,u);
}
}
void divide(int u)
{
vis[u]=true;d[0]=0;//不加会wa!
for(int i=head[u];i;i=edge[i].nxt)
{
int v=edge[i].v;int w=edge[i].w;
if(vis[v]) continue;
dis[v]=w;c[v]=1;
dfs(v,u);//处理计算v子树
add(v,u);//更新d
}
for(int i=head[u];i;i=edge[i].nxt)
{
int v=edge[i].v;
if(vis[v]) continue;
recover(v,u);//恢复d[]
}
for(int i=head[u];i;i=edge[i].nxt)
{
int v=edge[i].v;
if(vis[v]) continue;
smer=sim[v];MX=inf;getroot(v,0);//继续点分治
divide(root);
}
}
int main()
{
int n;
n=read();k=read();
int i,j;
for(i=1;i<n;i++)
{
int u,v,w;
u=read();v=read();w=read();u++;v++;
add(u,v,w);add(v,u,w);
}
memset(d,inf,sizeof(d));
MX=ans=inf;smer=n;
getroot(1,0);divide(root);
printf("%d\n",ans==inf?-1:ans);
return 0;
}