(\)
(Description)
给出一棵以(S)为根的(N)个节点的树,每条边都有花费的时间。
现在电源在(S)点,电流同时经过连接的边导向直接连接的点,之后的点都会将电流依次导向没有被电流到达过的点,定义不会继续向外传播电流的点为终点,现在有若干延时器,每一个可以使一条边花费的时间(+1),现要求所有终点被导到电的时间相同,问最少用多少延时器。
- (Nin [1,5 imes 10^5])
(\)
(Solution)
-
注意到越靠近根的边被延时,影响的叶子节点越多,所以我们要尽可能地让考上的边延时来达到要求。
-
首先一遍(DFS)求出最晚会被电流到达的点的时间,任务就是将所有的叶节点被导电的时间变为这个时间。第二遍(DFS)求最小代价。设(f[u])表示以(u)为根的子树最多共同需要延时的时长。之所以这么定义,是因为我们发现,对于子树内不同的延时需求,用当前点以上的边去满足,只能满足最小的需求,因为如果满足了更大的,会让需求小的超过了目标时间。
-
设(t[u])表示原来的树中该节点被导电的时间,(mx)表示全局需要达到的最晚时间点,对于叶子节点有(f[u]=mx-t[u]),对于其他的节点有(f[u]=min{ f[v] ig| vin son[u] }),同时对答案累加的是(sum f[v]-size[u] imes 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
",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
",ans);
return 0;
}