BZOJ 3677【Apio2014】连珠线 树形dp

题目描述:
在列奥纳多·达·芬奇时期,有一个流行的童年游戏,叫做“连珠线”。不出所料,玩这个游戏只需要珠子和线,珠子从1到礼编号,线分为红色和蓝色。游戏 开始时,只有1个珠子,而接下来新的珠子只能通过线由以下两种方式被加入:
1.Append(w,杪):-个新的珠子w和一个已有的珠子杪连接,连接使用红线。
2.Insert(w,u,v):-个新的珠子w加入到一对通过红线连接的珠子(u,杪)
之间,并将红线改成蓝线。也就是将原来u连到1的红线变为u连到w的蓝线与W连到V的蓝线。
无论红线还是蓝线,每条线都有一个长度。而在游戏的最后,将得到游戏的
最后得分:所有蓝线的长度总和。
现在有一个这个游戏的最终结构:你将获取到所有珠子之间的连接情况和所
有连线的长度,但是你并不知道每条线的颜色是什么。
你现在需要找到这个结构下的最大得分,也就是说:你需要给每条线一个颜
色f红色或蓝色),使得这种连线的配色方案是可以通过上述提到的两种连线方式
操作得到的,并且游戏得分最大。在本题中你只需要输出最大的得分即可。

题目分析:
我们根据题目给的要求操作,首先任选一个点当作根,然后进行1和2操作,那么我们可以发现所有蓝线得形式都是(当前节点,儿子,孙子)这样的。这样我们可以O(n)dp出来。

对于每个点设计状态: 这个点不是中转节点的最大值,这个点是中转节点的最大值。
最终得答案就是根不是中转节点的最大值。

但是每个点都有可能是根,所以枚举所有得点当根,时间复杂度是O(n^2)的,GG。

那我们就只做一边dp,然后换根,但是要求这个换根得操作很快。
对于当前根节点,我们要把根换成它得某个儿子(我就简称为目标根了=。=,我承认这个简称有点low)。

因为目标根是当前根得儿子,所以我们做第一次树dp得时候拿这个目标根更新了当前根,那我们把这一步骤撤销,就是把目标根对当前根的贡献减掉。
然后我们反过来有当前根得信息像正常树dp一样来更新目标根,这样就完成了换根。
(我们拿当前根来更新目标根得时候,当前根作为中转节点最大值这个状态很有可能是由目标根这里得到的,所以我们还需要存一个次大值)

注意mx并不是当前节点是中转节点的最大值,而是【这个节点是中转节点 】与【这个点不是中转节点】的差值,你要是问为什么这么存的话……因为你不扫描完全部得节点是无法直接求出【当前节点是中转节点的最大值】的。

代码如下:

#include <algorithm>
#include <cstdio>
#define N 220000
using namespace std;
const int INF=0x3f3f3f3f;
int n,ans;
int fir[N],nes[N<<1],v[N<<1],q[N<<1],tot=1;
int f[N],mx[N],mv[N],cm[N],cv[N];
void edge(int x,int y,int z)
{
    v[++tot]=y;
    q[tot]=z;
    nes[tot]=fir[x];
    fir[x]=tot;
    return;
}
#define edge(x,y,z) edge(x,y,z),edge(y,x,z)
void update(int c,int x,int v)
{
    if(v>mx[c])
    {
        cm[c]=mx[c]; cv[c]=mv[c];
        mx[c]=v;  mv[c]=x;
    }
    else if(v>cm[c])
    {
        cm[c]=v; cv[c]=x;
    }
}
void dfs(int c,int fa)
{
    f[c]=0; mx[c]=cm[c]=-INF; mv[c]=cv[c]=0;
    for(int t=fir[c];t;t=nes[t])
    {
        if(v[t]==fa) continue;
        dfs(v[t],c);
        int tmp=max(f[v[t]],f[v[t]]+mx[v[t]]+q[t]);
        f[c]+=tmp;
        update(c,v[t],-tmp+f[v[t]]+q[t]);
    }
}
void move_root(int c,int fa)
{
    ans=max(ans,f[c]);
    for(int t=fir[c];t;t=nes[t])
    {
        if(v[t]==fa) continue;
        int tmp=max(f[v[t]],f[v[t]]+mx[v[t]]+q[t]);
        int cur=max(f[c]-tmp,f[c]-tmp+(v[t]==mv[c]?cm[c]:mx[c])+q[t]);
        f[v[t]]+=cur;
        update(v[t],c,-cur-tmp+f[c]+q[t]);
        move_root(v[t],c);
    }
}
int main()
{
    scanf("%d",&n);
    for(int i=1,x,y,z;i<n;i++)
    {
        scanf("%d%d%d",&x,&y,&z);
        edge(x,y,z);
    }
    dfs(1,0);
    move_root(1,0);
    printf("%d\n",ans);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章