樹DP [ZJOI2008]騎士

問題 G: [ZJOI2008]騎士
時間限制: 1 Sec 內存限制: 162 MB
提交: 104 解決: 38
[提交][狀態][討論版]
題目描述
Z國的騎士團是一個很有勢力的組織,幫會中匯聚了來自各地的精英。他們劫富濟貧,懲惡揚善,受到社會各
界的讚揚。最近發生了一件可怕的事情,邪惡的Y國發動了一場針對Z國的侵略戰爭。戰火綿延五百里,在和平環境
中安逸了數百年的Z國又怎能抵擋的住Y國的軍隊。於是人們把所有的希望都寄託在了騎士團的身上,就像期待有一
個真龍天子的降生,帶領正義打敗邪惡。騎士團是肯定具有打敗邪惡勢力的能力的,但是騎士們互相之間往往有一
些矛盾。每個騎士都有且僅有一個自己最厭惡的騎士(當然不是他自己),他是絕對不會與自己最厭惡的人一同出
徵的。戰火綿延,人民生靈塗炭,組織起一個騎士軍團加入戰鬥刻不容緩!國王交給了你一個艱鉅的任務,從所有
的騎士中選出一個騎士軍團,使得軍團內沒有矛盾的兩人(不存在一個騎士與他最痛恨的人一同被選入騎士軍團的
情況),並且,使得這支騎士軍團最具有戰鬥力。爲了描述戰鬥力,我們將騎士按照1至N編號,給每名騎士一個戰
鬥力的估計,一個軍團的戰鬥力爲所有騎士的戰鬥力總和。

輸入
第一行包含一個正整數N,描述騎士團的人數。接下來N行,每行兩個正整數,按順序描述每一名騎士的戰鬥力
和他最痛恨的騎士。

輸出
應包含一行,包含一個整數,表示你所選出的騎士軍團的戰鬥力。

樣例輸入
3
10 2
20 3
30 1
樣例輸出
30
提示
N ≤ 1 000 000,每名騎士的戰鬥力都是不大於 1 000 000的正整數。

這玩意說仇恨是單向的,但實際上它是雙向的,兩個騎士只能二選一。
首先假設它只有樹,那麼DP方程就很好想了,f[i][0]表示第i個點及其子樹切不選它本身的最優方案,f[i][1]同理,樹DP一下就很簡單了。
但是它肯定是樹套環。但是一棵樹上只會有一個環。考慮把環拆掉。也就是固定砍斷一條邊,強迫一個端點不能選,從另一個端點開始樹DP.之後交換兩個點,再跑一邊,就可以得出這個樹套環對答案的最大貢獻了。

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<iostream>
#define N 1000005
#define ll long long
using namespace std;
struct road{int v,next;}lu[N*2];
int read()
{
     int sum=0,f=1;char x=getchar();
     while(x<'0'||x>'9'){if(x=='-')f=-1;x=getchar();}
     while(x>='0'&&x<='9'){sum=(sum<<3)+(sum<<1)+x-'0';x=getchar();}
     return sum*f;
}
int n,e,st,ed,adj[N],vis[N],cut[N];ll w[N],f[N][2],ans;
void add(int u,int v){lu[e]=(road){v,adj[u]};adj[u]=e++;}
void dp(int x,int fa,int y)
{
     f[x][0]=0;vis[x]=1;
     if(x!=y)f[x][1]=w[x];
     else f[x][1]=0;
     for(int i=adj[x];~i;i=lu[i].next)
     {
         int to=lu[i].v;if(to==fa||cut[i]==-1)continue;
         dp(to,x,y);
         f[x][0]+=max(f[to][1],f[to][0]);
         f[x][1]+=f[to][0];
     }
}
void dfs(int x,int fa)
{
     vis[x]=1;
     for(int i=adj[x];~i;i=lu[i].next)
     {
         int to=lu[i].v;if(to==fa)continue;
         if(vis[to])
         {
              st=x;ed=to;
              cut[i]=cut[i^1]=-1;
              return;
         }
         dfs(to,x);
     }
}
int main()
{
    scanf("%d",&n);int x;
    memset(adj,-1,sizeof(adj));
    for(int i=1;i<=n;i++)
    {
        scanf("%lld%d",&w[i],&x);
        add(i,x);add(x,i); 
    }
    for(int i=1;i<=n;i++)
       if(!vis[i])
       {
           dfs(i,0);ll k=0;
           dp(st,0,ed);
           k=max(f[st][0],f[st][1]);
           dp(ed,0,st);
           k=max(k,max(f[ed][0],f[ed][1]));
           ans+=k;
       }
    printf("%lld\n",ans);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章