[ ZJOI 2007 ] 时态同步

\(\\\)

\(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;
}

posted @ 2018-09-24 09:39  SGCollin  阅读(200)  评论(0编辑  收藏  举报