[BZOJ1040][ZJOI2008]騎士(環套樹dp)
阿新 • • 發佈:2018-02-22
zoj 以及 rip type 接下來 描述 discus des 之間
1040: [ZJOI2008]騎士
Time Limit: 10 Sec Memory Limit: 162 MB
Submit: 5816 Solved: 2263
[Submit][Status][Discuss]Description
Z國的騎士團是一個很有勢力的組織,幫會中匯聚了來自各地的精英。他們劫富濟貧,懲惡揚善,受到社會各
界的贊揚。最近發生了一件可怕的事情,邪惡的Y國發動了一場針對Z國的侵略戰爭。戰火綿延五百裏,在和平環境
中安逸了數百年的Z國又怎能抵擋的住Y國的軍隊。於是人們把所有的希望都寄托在了騎士團的身上,就像期待有一
個真龍天子的降生,帶領正義打敗邪惡。騎士團是肯定具有打敗邪惡勢力的能力的,但是騎士們互相之間往往有一
些矛盾。每個騎士都有且僅有一個自己最厭惡的騎士(當然不是他自己),他是絕對不會與自己最厭惡的人一同出
征的。戰火綿延,人民生靈塗炭,組織起一個騎士軍團加入戰鬥刻不容緩!國王交給了你一個艱巨的任務,從所有
的騎士中選出一個騎士軍團,使得軍團內沒有矛盾的兩人(不存在一個騎士與他最痛恨的人一同被選入騎士軍團的
情況),並且,使得這支騎士軍團最具有戰鬥力。為了描述戰鬥力,我們將騎士按照1至N編號,給每名騎士一個戰
鬥力的估計,一個軍團的戰鬥力為所有騎士的戰鬥力總和。Input
第一行包含一個正整數N,描述騎士團的人數。接下來N行,每行兩個正整數,按順序描述每一名騎士的戰鬥力
和他最痛恨的騎士。Output
應包含一行,包含一個整數,表示你所選出的騎士軍團的戰鬥力。
Sample Input
3
10 2
20 3
30 1Sample Output
30HINT
N ≤ 1 000 000,每名騎士的戰鬥力都是不大於 1 000 000的正整數。
Source
[Submit][Status][Discuss]
每個點有且僅有一個出邊,說明每個連通分量(有向連通)都是有且僅有一個環,以及環邊的樹(樹根必定在環上),這就是基環外向樹問題。
感覺有點像仙人球的超弱化版?直接對一個每遍歷過的點,找到所在的連通分量的環,然後
1 #include<cstdio> 2 #include<algorithm> 3 #define rep(i,l,r) for (int i=l; i<=r; i++) 4 #define For(i,x) for (int i=h[x],k; i; i=nxt[i]) 5 typedef long long ll; 6 using namespace std; 7 8 const int N=2000100,inf=1000000000; 9 int n,cnt,rt,fa[N],val[N],vis[N],to[N],nxt[N],h[N]; 10 ll f[N][2],ans; 11 void add(int u,int v){ to[++cnt]=v; nxt[cnt]=h[u]; h[u]=cnt; } 12 13 void dfs(int x){ 14 vis[x]=1; f[x][0]=0; f[x][1]=val[x]; 15 For(i,x) if ((k=to[i])!=rt) 16 dfs(k),f[x][0]+=max(f[k][1],f[k][0]),f[x][1]+=f[k][0]; 17 else f[k][1]=-inf; 18 } 19 20 void find(int x){ 21 vis[x]=1; rt=x; 22 while (!vis[fa[rt]]) rt=fa[rt],vis[rt]=1; 23 dfs(rt); ll t=max(f[rt][0],f[rt][1]); 24 vis[rt]=1; rt=fa[rt]; dfs(rt); ans+=max(t,max(f[rt][0],f[rt][1])); 25 } 26 27 int main(){ 28 freopen("P2607.in","r",stdin); 29 freopen("P2607.out","w",stdout); 30 scanf("%d",&n); 31 rep(i,1,n) scanf("%d",&val[i]),scanf("%d",&fa[i]),add(fa[i],i); 32 rep(i,1,n) if (!vis[i]) find(i); 33 printf("%lld\n",ans); 34 return 0; 35 }
[BZOJ1040][ZJOI2008]騎士(環套樹dp)