标签:amp return span std clu turn AC == main
假设当前分治到$x$,记$t_i$为到$x$距离为$i$的点中,到$x$最少的边数,那么我们采用分治2:每次统计一个儿子与集合中点产生的贡献,并把儿子信息合并到集合中,假设当前统计到$u$,$u$到$x$的边数为$d$,那么用$d+t_{k-dis_{x,u}}$更新答案即可,注意最后重置$t_i$时要遍历子树而不是memset,不然无法保证复杂度
#include<stdio.h>
const int inf=100000000;
int min(int a,int b){return a<b?a:b;}
int max(int a,int b){return a>b?a:b;}
int h[200010],to[400010],nex[400010],w[400010],M;
void add(int a,int b,int c){
M++;
to[M]=b;
w[M]=c;
nex[M]=h[a];
h[a]=M;
}
bool v[200010];
int siz[200010],n;
void dfs1(int fa,int x){
n++;
siz[x]=1;
for(int i=h[x];i;i=nex[i]){
if(!v[to[i]]&&to[i]!=fa){
dfs1(x,to[i]);
siz[x]+=siz[to[i]];
}
}
}
int mn,cn;
void dfs2(int fa,int x){
int i,k;
k=0;
for(i=h[x];i;i=nex[i]){
if(!v[to[i]]&&to[i]!=fa){
dfs2(x,to[i]);
k=max(k,siz[to[i]]);
}
}
k=max(k,n-siz[x]);
if(k<mn){
mn=k;
cn=x;
}
}
int t[1000010],k,ans;
void dfs3(int fa,int x,int dep,int dis){
if(dis<=k)ans=min(ans,dep+t[k-dis]);
for(int i=h[x];i;i=nex[i]){
if(!v[to[i]]&&to[i]!=fa)dfs3(x,to[i],dep+1,dis+w[i]);
}
}
void dfs4(int fa,int x,int dep,int dis){
if(dis<=k)t[dis]=min(t[dis],dep);
for(int i=h[x];i;i=nex[i]){
if(!v[to[i]]&&to[i]!=fa)dfs4(x,to[i],dep+1,dis+w[i]);
}
}
void dfs5(int fa,int x,int dis){
if(dis<=k)t[dis]=inf;
for(int i=h[x];i;i=nex[i]){
if(!v[to[i]]&&to[i]!=fa)dfs5(x,to[i],dis+w[i]);
}
}
void solve(int x){
int i;
n=0;
dfs1(0,x);
mn=inf;
dfs2(0,x);
x=cn;
t[0]=0;
for(i=h[x];i;i=nex[i]){
if(!v[to[i]]){
dfs3(x,to[i],1,w[i]);
dfs4(x,to[i],1,w[i]);
}
}
dfs5(0,x,0);
v[x]=1;
for(i=h[x];i;i=nex[i]){
if(!v[to[i]])solve(to[i]);
}
}
int main(){
int n,i,a,b,c;
scanf("%d%d",&n,&k);
for(i=1;i<n;i++){
scanf("%d%d%d",&a,&b,&c);
a++;
b++;
add(a,b,c);
add(b,a,c);
}
for(i=0;i<=k;i++)t[i]=inf;
ans=inf;
solve(1);
if(ans==inf)ans=-1;
printf("%d",ans);
}
标签:amp return span std clu turn AC == main
原文地址:https://www.cnblogs.com/jefflyy/p/8966858.html