• [ ZJOI 2007 ] 时态同步


    (\)

    (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;
    }
    
    
  • 相关阅读:
    curl post
    mysql存储引擎
    梳理版本控制器:SVN和Git比较
    详细说明php的4中开源框架(TP,CI,Laravel,Yii)
    五种常见的 PHP 设计模式
    php+ajax实现跨域单点登录
    laravel
    Gitlab配置webhooks实现自动化部署
    linux CentOs7 安装gitlab
    身份证验证
  • 原文地址:https://www.cnblogs.com/SGCollin/p/9694810.html
Copyright © 2020-2023  润新知