Z國的騎士團是一個頗有勢力的組織,幫會中匯聚了來自各地的精英。ios
他們劫富濟貧,懲惡揚善,受到社會各界的讚賞。c++
最近發生了一件可怕的事情,邪惡的Y國發動了一場針對Z國的侵略戰爭。spa
戰火綿延五百里,在和平環境中安逸了數百年的Z國又怎能抵擋的住Y國的軍隊。blog
因而人們把全部的但願都寄託在了騎士團的身上,就像期待有一個真龍天子的降生,帶領正義戰勝邪惡。ip
騎士團是確定具備戰勝邪惡勢力的能力的,可是騎士們互相之間每每有一些矛盾。ci
每一個騎士都有且僅有一個本身最厭惡的騎士(固然不是他本身),他是絕對不會與本身最厭惡的人一同出征的。get
戰火綿延,人民生靈塗炭,組織起一個騎士軍團加入戰鬥刻不容緩!it
國王交給了你一個艱鉅的任務,從全部的騎士中選出一個騎士軍團,使得軍團內沒有矛盾的兩人(不存在一個騎士與他最痛恨的人一同被選入騎士軍團的狀況),而且,使得這支騎士軍團最具備戰鬥力。io
爲了描述戰鬥力,咱們將騎士按照1至N編號,給每名騎士一個戰鬥力的估計,一個軍團的戰鬥力爲全部騎士的戰鬥力總和。
第一行包含一個正整數N,描述騎士團的人數。接下來N行,每行兩個正整數,按順序描述每一名騎士的戰鬥力和他最痛恨的騎士。
應包含一行,包含一個整數,表示你所選出的騎士軍團的戰鬥力。
N ≤ 1 000 000,每名騎士的戰鬥力都是不大於 1 000 000的正整數。
題解Here!
首先,須要先寫出這題:
這題就是一個裸的樹形$DP$對吧。
再回到原題。
這是一個基環樹上的樹形$DP$。
也就是把那個題放到了基環樹上。。。
那,什麼是基環樹呢?
基環樹,又稱環套樹,即一個無向圖中只存在一個環,而且環上的每一個點都是一棵樹的樹根。
咱們把$x$所討厭的人$y$設爲$x$的父親節點,這樣考慮每個人都有且只有一條出邊。
因此對一個"聯通塊",只有根節點有機會造成環,即環必定包含根節點。
而後考慮非根節點:
它的出度必定是貢獻給它的父親的。
而根節點它的出度只能貢獻給它的後代。
因此咱們又解決了一個問題:每一個聯通塊內有且只有一個簡單環。
這樣,咱們考慮把每一個聯通塊的環上刪一條邊,這樣它必然構成樹。
而後要注意:刪掉的邊所鏈接的兩點$x,y$是不能同時選的。
因此咱們分別強制$x,y$其中一個點不選,對新樹跑$DP$。
同$P1352$,設$dp[i][0/1]$表示以$i$爲根節點的子樹$\text{選i}/\text{不選i}$所能得到的最大價值。
顯然相鄰的點是不能選的。
因此獲得狀態轉移方程:
$$dp[i][0]=\sum_{j\in son_i}(\max\left\{\begin{array}{}dp[j][0]\\dp[j][1]\end{array}\right.)$$
$$dp[i][1]=\sum_{j\in son_i}dp[j][0])$$
注意:記得開$long\ long$。
頭一次寫基環樹,感受就是仙人掌的退化版啊。。。
附代碼:
#include<iostream>
#include<algorithm>
#include<cstdio>
#define MAXN 1000010
#define MAX (1LL<<62)
using namespace std;
int n,c=1;
int head[MAXN],val[MAXN],fa[MAXN];
long long ans=0,dp[MAXN][2];
bool vis[MAXN];
struct Edge{
int next,to;
}a[MAXN];
inline int read(){
int date=0,w=1;char c=0;
while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
return date*w;
}
inline void add_edge(int x,int y){
a[c].to=y;a[c].next=head[x];head[x]=c++;
}
void dfs(int rt,int f){
vis[rt]=true;
dp[rt][0]=0;dp[rt][1]=val[rt];
for(int i=head[rt];i;i=a[i].next){
int will=a[i].to;
if(will!=f){
dfs(will,f);
dp[rt][0]+=max(dp[will][0],dp[will][1]);
dp[rt][1]+=dp[will][0];
}
else dp[will][1]=-MAX;
}
}
void find_circle(int x){
int rt=x;
vis[x]=true;
while(!vis[fa[rt]]){
rt=fa[rt];
vis[rt]=true;
}
dfs(rt,rt);
long long t=max(dp[rt][0],dp[rt][1]);
vis[rt]=true;
rt=fa[rt];
dfs(rt,rt);
ans+=max(t,max(dp[rt][0],dp[rt][1]));
}
void work(){
for(int i=1;i<=n;i++)if(!vis[i])find_circle(i);
printf("%lld\n",ans);
}
void init(){
n=read();
for(int i=1;i<=n;i++){
val[i]=read();fa[i]=read();
add_edge(fa[i],i);
}
}
int main(){
init();
work();
return 0;
}