\(\\\)
\(Description\)
给出一棵以\(S\)为根的\(N\)个节点的树,每条边都有花费的时间。
现在电源在\(S\)点,电流同时经过连接的边导向直接连接的点,之后的点都会将电流依次导向没有被电流到达过的点,定义不会继续向外传播电流的点为终点,现在有若干延时器,每一个可以使一条边花费的时间\(+1\),现要求所有终点被导到电的时间相同,问最少用多少延时器。
- \(N\in [1,5\times 10^5]\)
\(\\\)
\(Solution\)
注意到越靠近根的边被延时,影响的叶子节点越多,所以我们要尽可能地让考上的边延时来达到要求。
首先一遍\(DFS\)求出最晚会被电流到达的点的时间,任务就是将所有的叶节点被导电的时间变为这个时间。第二遍\(DFS\)求最小代价。设\(f[u]\)表示以\(u\)为根的子树最多共同需要延时的时长。之所以这么定义,是因为我们发现,对于子树内不同的延时需求,用当前点以上的边去满足,只能满足最小的需求,因为如果满足了更大的,会让需求小的超过了目标时间。
设\(t[u]\)表示原来的树中该节点被导电的时间,\(mx\)表示全局需要达到的最晚时间点,对于叶子节点有\(f[u]=mx-t[u]\),对于其他的节点有\(f[u]=min\{\ f[v]\ \big|\ v\in son[u]\ \}\),同时对答案累加的是\(\sum f[v]-size[u]\times f[u]\)。
还有一个问题,最后答案并不需要累加上根节点的延时需求。因为注意到每一个节点的需求是一路取\(min\)上来的,所以最大点取\(min\)上来一定会将根节点的答案变为\(0\)。
换一个角度一遍\(DFS\)也可以做。我们只关心局部的答案。每次先扫描一遍求出子树\(max\ t\),那么其他部分都需要调成跟这个节点同一个时间,直接在到这个子树的边上使用延时器就好。
我NC方法又麻烦了
\(\\\)
\(Code\)
两遍\(DFS\)超麻烦\(NC\)写法
#include<cmath>
#include<cstdio>
#include<cctype>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define N 500010
#define R register
#define gc getchar
#define inf 90000000000000000ll
using namespace std;
typedef long long ll;
inline int rd(){
int x=0; bool f=0; char c=gc();
while(!isdigit(c)){if(c=='-')f=1;c=gc();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=gc();}
return f?-x:x;
}
ll ans,m,f[N];
int n,s,tot,hd[N];
struct edge{int w,to,nxt;}e[N<<1];
inline void add(int u,int v,int w){
e[++tot].to=v; e[tot].w=w;
e[tot].nxt=hd[u]; hd[u]=tot;
}
inline void dfs1(int u,int fa){
m=max(m,f[u]);
for(R int i=hd[u],v;i;i=e[i].nxt)
if((v=e[i].to)!=fa) f[v]=f[u]+e[i].w,dfs1(v,u);
}
inline void dfs2(int u,int fa){
ll tmp=inf,sum=0,cnt=0;
for(R int i=hd[u],v;i;i=e[i].nxt)
if((v=e[i].to)!=fa){
++cnt; dfs2(v,u);
tmp=min(tmp,f[v]); sum+=f[v];
}
cnt!=0?ans+=sum-tmp*cnt,f[u]=tmp:f[u]=m-f[u];
}
int main(){
n=rd(); s=rd();
for(R int i=1,u,v,w;i<n;++i){
u=rd(); v=rd(); w=rd();
add(u,v,w); add(v,u,w);
}
dfs1(s,0);
dfs2(s,0); ans+=f[s];
printf("%lld\n",ans);
return 0;
}
一遍\(DFS\)
#include<cmath>
#include<cstdio>
#include<cctype>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define N 500010
#define R register
#define gc getchar
using namespace std;
typedef long long ll;
inline int rd(){
int x=0; bool f=0; char c=gc();
while(!isdigit(c)){if(c=='-')f=1;c=gc();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=gc();}
return f?-x:x;
}
ll ans,m,f[N];
int n,s,tot,hd[N];
struct edge{int w,to,nxt;}e[N<<1];
inline void add(int u,int v,int w){
e[++tot].to=v; e[tot].w=w;
e[tot].nxt=hd[u]; hd[u]=tot;
}
inline void dfs(int u,int fa){
ll mx=0ll;
for(R int i=hd[u],v;i;i=e[i].nxt)
if((v=e[i].to)!=fa){
f[v]=f[u]+e[i].w;
dfs(v,u); mx=max(mx,f[v]);
}
for(R int i=hd[u],v;i;i=e[i].nxt)
if((v=e[i].to)!=fa) ans+=mx-f[v];
f[u]=max(mx,f[u]);
}
int main(){
n=rd(); s=rd();
for(R int i=1,u,v,w;i<n;++i){
u=rd(); v=rd(); w=rd();
add(u,v,w); add(v,u,w);
}
dfs(s,0);
printf("%lld\n",ans);
return 0;
}