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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章