[ZJOI2007]时态同步

洛咕

题意:给定一棵(N(N<=500000))个节点的树以及树根编号,每次操作可以使得一条边的边权加1,求使得根节点到所有叶子节点距离相同需要的最少操作次数.

分析:显然当一棵子树内的所有叶子节点到根节点的路径都需要增加边权时,给越上面的边增加边权越好.但是我们发现如果从树根往下处理,判断,加边权后的路径距离更新等等并不太好实现.

考虑从叶子节点往上推,这也是树形DP的一般过程.为什么从叶子节点往上推会比较好处理呢,试想一棵3个节点的子树,就是一个父亲节点,下面两个儿子节点,我们只要求出两个叶子节点分别到父亲节点的距离,记为(f[i]),那么距离小的那个一定需要增加边权.也就是说从下往上推,每一步判断是否增加边权,增加多少边权,都是可以确定的.

不开long long会WA最后两个点.

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#define ll long long
using namespace std;
inline int read(){
    int x=0,o=1;char ch=getchar();
    while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
    if(ch=='-')o=-1,ch=getchar();
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*o;
}
const int N=500005;
ll ans,f[N];
int tot,head[N],nxt[N*2],to[N*2],w[N*2];
inline void add(int a,int b,int c){
	nxt[++tot]=head[a];head[a]=tot;
	to[tot]=b;w[tot]=c;
}
inline void dfs(int u,int fa){
	for(int i=head[u];i;i=nxt[i]){
		int v=to[i];
		if(v==fa)continue;
		dfs(v,u);
		f[u]=max(f[u],f[v]+w[i]);//找到最大距离
	}
	for(int i=head[u];i;i=nxt[i]){
		int v=to[i];
		if(v==fa)continue;
		ans+=f[u]-(f[v]+w[i]);//增加边权
	}
}
int main(){
	int n=read(),root=read();
	for(int i=1;i<n;++i){
		int a=read(),b=read(),c=read();
		add(a,b,c);add(b,a,c);
	}
	dfs(root,0);
	printf("%lld
",ans);
    return 0;
}

原文地址:https://www.cnblogs.com/PPXppx/p/11311988.html