1. 程式人生 > >[ZJOI2008] 騎士

[ZJOI2008] 騎士

mem ++ 測試數據 div 解決 std 接下來 bool scrip

Description

Z國的騎士團是一個很有勢力的組織,幫會中匯聚了來自各地的精英。他們劫富濟貧,懲惡揚善,受到社會各界的贊揚。

最近發生了一件可怕的事情,邪惡的Y國發動了一場針對Z國的侵略戰爭。戰火綿延五百裏,在和平環境中安逸了數百年的Z國又怎能抵擋的住Y國的軍隊。於是人們把所有的希望都寄托在了騎士團的身上,就像期待有一個真龍天子的降生,帶領正義打敗邪惡。

騎士團是肯定具有打敗邪惡勢力的能力的,但是騎士們互相之間往往有一些矛盾。每個騎士都有且僅有一個自己最厭惡的騎士(當然不是他自己),他是絕對不會與自己最厭惡的人一同出征的。

戰火綿延,人民生靈塗炭,組織起一個騎士軍團加入戰鬥刻不容緩!國王交給了你一個艱巨的任務,從所有的騎士中選出一個騎士軍團,使得軍團內沒有矛盾的兩人(不存在一個騎士與他最痛恨的人一同被選入騎士軍團的情況),並且,使得這支騎士軍團最具有戰鬥力。

為了描述戰鬥力,我們將騎士按照1至N編號,給每名騎士一個戰鬥力的估計,一個軍團的戰鬥力為所有騎士的戰鬥力總和。

Input

第一行包含一個正整數N,描述騎士團的人數。

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

Output

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

Range

對於30%的測試數據,滿足N ≤ 10;

對於60%的測試數據,滿足N ≤ 100;

對於80%的測試數據,滿足N ≤ 10 000。

對於100%的測試數據,滿足N ≤ 1 000 000,每名騎士的戰鬥力都是不大於 1 000 000的正整數。

Solution

觀察到根據題意連好邊之後形成了一個基環樹森林。

對於這類基環樹問題,一般的解決方案如下:

在一個聯通塊內找到唯一的環

隨意刪去環上的一條邊,記錄兩個端點 $first_root$ $second_root$

分別以兩個端點為根做樹形DP

取最大值

Code

#include<cstdio>
#include<cstring>
#include<iostream>
#define N 1000005
#define int long long

int val[N];
int n,cnt=1;
bool vis[N];
int head[N];
int f[N],g[N];
int first_root,second_root,deleedge;

struct Edge{ int to,nxt; }edge[N<<1]; void add(int x,int y){ edge[++cnt].to=y; edge[cnt].nxt=head[x]; head[x]=cnt; } void dfs(int now,int in_edge){ vis[now]=1; for(int i=head[now];i;i=edge[i].nxt){ int to=edge[i].to; if(!vis[to]) dfs(to,i); else if((i^1)!=in_edge){ first_root=now; second_root=to; deleedge=i; } } } void dp(int now,int in_edge){ f[now]=val[now]; g[now]=0; for(int i=head[now];i;i=edge[i].nxt){ int to=edge[i].to; if(i==deleedge||(i^1)==deleedge) continue; if(i==in_edge||(i^1)==in_edge) continue; dp(to,i); g[now]+=std::max(g[to],f[to]); f[now]+=g[to]; //printf("now=%lld,to=%lld,g=%lld,f=%lld\n",now,to,g[now],f[now]); } } signed main(){ scanf("%lld",&n); for(int x,i=1;i<=n;i++){ scanf("%lld%lld",&val[i],&x); add(x,i); add(i,x); } int ans=0; for(int i=1;i<=n;i++){ if(vis[i]) continue; dfs(i,0); /*memset(f,0,sizeof f); memset(g,0,sizeof g);*/ dp(first_root,0); int maxn=-12345678901234567; maxn=std::max(maxn,g[first_root]); /*memset(f,0,sizeof f); memset(g,0,sizeof g);*/ dp(second_root,0); //printf("frist=%lld,second=%lld\n",first_root,second_root); maxn=std::max(maxn,g[second_root]); ans+=maxn; } printf("%lld\n",ans); return 0; }

[ZJOI2008] 騎士