bzoj 1060

这题其实有点骗人...

通过观察很容易发现:考虑某一些叶节点的LCA,由于根节点到这个LCA的距离唯一,故要求这些叶节点到这一LCA的距离都相等

于是我们仅需dfs,找到次底层的节点,然后使这些节点的子节点到这些节点的距离都相等即可

再向上回溯,算法完全相同,仅需把下面的距离累计到该节点向上的边即可

用图理解一下:

如图所示,所有蓝边长度应当相同,红边长度相同,绿边长度相同

那么我们就找出蓝边中长度最长的一个,然后将所有边长变成他就可以了

然后向上回溯:

如图所示,将蓝边边权累计到黄边上,将红边累积到紫边上,将绿边累计到橙边上,然后令黄边,紫边,橙边长度相同即可

贴代码:

#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <queue>
#include <stack>
#define ll long long
using namespace std;
struct Edge
{
	int next;
	int to;
	int val;
}edge[1000005];
int head[500005];
int dis[500005];
int cnt=1;
int f[500005];
void add(int l,int r,int w)
{
	edge[cnt].to=r;
	edge[cnt].next=head[l];
	edge[cnt].val=w;
	head[l]=cnt++;
}
void dfs(int x,int fx)//O(n)
{
	f[x]=fx;
	for(int i=head[x];i!=-1;i=edge[i].next)
	{
		int to=edge[i].to;
		if(to==fx)
		{
			continue;
		}
		dfs(to,x);
	}
}
ll tot=0;
void dfs2(int rt,int frt)//O(n*log2n)
{
	for(int i=head[rt];i!=-1;i=edge[i].next)//O(n*log2n)
	{
		int to=edge[i].to;
		if(to==frt)
		{
			continue;
		}
		dfs2(to,rt);
	}
	priority_queue <int> M;
	for(int i=head[rt];i!=-1;i=edge[i].next)//O(log2n)
	{
		int to=edge[i].to;
		if(to==frt)
		{
			continue;
		}
		M.push(edge[i].val);
	}
	if(!M.empty())
	{
		int l=M.top();
		M.pop();
		while(!M.empty())//O(n*log2n)
		{
			int l1=M.top();
			M.pop();
			tot+=(ll)l-l1;
		}
		for(int i=head[frt];i!=-1;i=edge[i].next)
		{
			int to=edge[i].to;
			if(to==rt)
			{
				edge[i].val+=l;
				break;
			}
		}
	}
}
int main()
{
	memset(head,-1,sizeof(head));
	int n;
	scanf("%d",&n);
	int rt;
	scanf("%d",&rt);
	for(int i=1;i<n;i++)
	{
		int x,y,v;
		scanf("%d%d%d",&x,&y,&v);
		add(x,y,v);
		add(y,x,v);
	}
	dfs(rt,rt);
	dfs2(rt,rt);
	printf("%lld
",tot);
	return 0;
}
原文地址:https://www.cnblogs.com/zhangleo/p/10764200.html