P1131 [ZJOI2007]时态同步

传送门

DP

题目讲得很清楚"对于电路板的任何两个节点,都存在且仅存在一条通路".

所以电路板是一颗树

题目要求叶子节点时态一致

如果从根开始枚举时间肯定超时

考虑反过来

从叶子节点开始考虑时间

容易注意到

对于一个子树的根 root 来说

不管上面用了多久把信息传下来

要让它的儿子收到信息的时间相同

必须让 root 到各个儿子的距离相等

如果一个子树已经调好了

那么以后也不会再去更改

无后效性

所以考虑DP

对于一个非叶子节点,要让它到各个儿子的距离相等

那么显然最节省的方案就是

让它到各个儿子的距离改成它最远的儿子的距离

所以得出状态转移方程:

设f[ i ]为 当以 i 为根的子树的时态同步时,从它的叶子到 i 需要的时间

f[ i ]=max(f[ i ],f[ u ]+dis[ i ][ u ]) ( u为 i 的儿子,dis[ i ][ u ]为 i 到 u 的距离)

而答案就在更新f[ i ] 的时候顺便累加一下就好了

 

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
using namespace std;
inline int read()
{
    int x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9')
    {
        if(ch=='-') f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9')
    {
        x=(x<<1)+(x<<3)+(ch^48);
        ch=getchar();
    }
    return x*f;
}
const int N=500007;
vector <int> v[N],g[N]; //用vector 存图,反正不会超时
int n,rt;//rt 为根节点
long long f[N],ans;//f如上所述,ans存答案
inline void dfs(int x,int fa)//fa 存 x 的父亲
{
    int len=v[x].size();
    for(int i=0;i<len;i++)
    {
        int u=v[x][i];
        if(u==fa) continue;//如果是父亲就跳过
        dfs(u,x);//否则先更新儿子节点
        f[x]=max(f[x],f[u]+g[x][i]);//更新当前节点
    }
    for(int i=0;i<len;i++)
    {
        int u=v[x][i];
        if(u==fa) continue;//如果是父亲就跳过
        ans+=(f[x]-f[u]-g[x][i]);//累加答案
    }
}
int main()
{
    int a,b,c;
    cin>>n>>rt;
    for(int i=1;i<n;i++)
    {
        a=read(); b=read(); c=read();
        v[a].push_back(b); g[a].push_back(c);
        v[b].push_back(a); g[b].push_back(c);
    }//存边
    dfs(rt,rt);
    cout<<ans;
    return 0;
}

 

posted @ 2018-08-23 15:10  LLTYYC  阅读(214)  评论(0编辑  收藏  举报