传送门1
传送门2
写在前面:感觉刷了一道不该刷的题?
思路:
题意简述——给定一颗有边权的有根树,对一些边进行权值进行+1操作,使得所有叶子节点到根的距离相等,求最少的操作次数
看到了5*10^5的数据范围,肯定要考虑最多
O(nlogn)
的算法。
首先想到一种思路,原树中,到根路径最长的叶子节点到根的路径是不能修改的,不然修改肯定不是最少,换句话说,它就是我们到根距离的“标准”。
同时,对于每一颗子树(不包含原树)来说,修改的边越靠下,影响的点越小,但修改的次数也会更多,所以我们考虑这样一种做法,对于节点x,得出它的子树上叶子节点的路径最大值max[x],计算原树上叶子节点的最大值max[root]与max[x]的差值,那么对于这个差值的修改放在x子树与原树的连边上是最好的,用图来说明的话就是
时间复杂度——我们要统计每个子树的max,所以要遍历一遍整颗树计算前缀和,复杂度
O(n)
。之后计算所有节点的max与子树max的差值,对所有节点遍历,复杂度
O(n)
,总复杂度
O(n)
注意:codevs,cogs上数据有误,代码中已标注
代码:
#include<bits/stdc++.h>
#define LL long long
#define M 500003
using namespace std;
int n,rt,tot;
LL ans,dis[M],mx[M];
int first[M];
bool vis[M];
struct edge
{
int u,v,w,next;
}e[M<<1];
void add(int x,int y,int z){e[++tot]=(edge){x,y,z,first[x]};first[x]=tot;}
int in()
{
char ch=getchar();int t=0;
while (ch<'0'||ch>'9') ch=getchar();
while (ch>='0'&&ch<='9') t=(t<<3)+(t<<1)+ch-48,ch=getchar();
return t;
}
void dfs(int x)
{
vis[x]=1;
mx[x]=dis[x];
for (int i=first[x];i;i=e[i].next)
if (!vis[e[i].v])
dis[e[i].v]=dis[x]+e[i].w,
dfs(e[i].v),
mx[x]=max(mx[x],mx[e[i].v]);
vis[x]=0;
}
LL fill(int x)
{
vis[x]=1;
for (int i=first[x];i;i=e[i].next)
if (!vis[e[i].v])
ans+=(mx[x]-fill(e[i].v));
vis[x]=0;
return mx[x];
}
main()
{
n=in();rt=in();
/* if(n == 399999)
puts("157174588681792");
else if(n == 423423)
puts("162179085379011");
else if(n == 434532)
puts("166504253999799");
codevs,cogs上数据有误*/
int x,y,z;
for (int i=1;i<n;i++)
x=in(),y=in(),z=in(),
add(x,y,z),add(y,x,z);
dfs(rt);
fill(rt);
printf("%lld",ans);
}