[BZOJ2599]Race

假设当前分治到$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);
}

猜你喜欢

转载自www.cnblogs.com/jefflyy/p/8966858.html