Luogu P1131 [ZJOI2007]时态同步

题目链接:Click here

Solution:

题目描述十分冗长,实际上就是给你一棵树,问你最少增加多少次边权,使得所有叶子节点到根节点距离相等

有一个十分显然的结论:最后的距离为不增加边权的最大距离

则我们可设\(f[x]\)表示\(x\)节点与\(fa[x]\)相连的边需要加多少权值

易得状态转移:\(f[x]=min\{ f[v]|v\in son_x \}\)

最后对每个点统计答案:\(ans+=f[x]-f[fa[x]]\)

要开long long

Code:

//P1131 Treedp
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=5e5+1;
const int inf=21474836470000;
int n,rt,cnt,head[N];
int ans,maxdis,dis[N],f[N];
struct Edge{int nxt,to,val;}edge[N<<1];
void ins(int x,int y,int z){
	edge[++cnt].nxt=head[x];
	edge[cnt].to=y;head[x]=cnt;
	edge[cnt].val=z;
}
void getdis(int x,int fa){
	for(int i=head[x];i;i=edge[i].nxt){
		int y=edge[i].to;
		if(y==fa) continue;
		dis[y]=dis[x]+edge[i].val;
		getdis(y,x);maxdis=max(maxdis,dis[y]);
	}
}
void dfs(int x,int fa){
	f[x]=inf;int flag=0;
	for(int i=head[x];i;i=edge[i].nxt){
		int y=edge[i].to;
		if(y==fa) continue;
		dfs(y,x);f[x]=min(f[x],f[y]);
		flag=1;
	}
	if(!flag) f[x]=maxdis-dis[x];
}
void calc(int x,int fa){
	ans+=f[x]-f[fa];
	for(int i=head[x];i;i=edge[i].nxt){
		int y=edge[i].to;
		if(y==fa) continue;
		calc(y,x);	
	}
}
int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-f;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
signed main(){
	n=read(),rt=read();
	for(int i=1;i<n;i++){
		int x=read(),y=read(),z=read();
		ins(x,y,z),ins(y,x,z);
	}getdis(rt,0);
	dfs(rt,0);calc(rt,0);
	printf("%lld\n",ans);
	return 0;
}
posted @ 2019-09-07 19:54  DQY_dqy  阅读(91)  评论(0编辑  收藏  举报