- 顧名思義,即樹上的
dp
問題。 - 有兩種轉移方式:一是從根轉移到葉子,二是從葉子轉移到根。在實際應用中,從葉子轉移到根的方式應用的更多。
- 樹上
dp
在實現起來的時候,可能需要配合貪心等其它高效的算法,甚至於線段樹
等高效數據結構。 - 代碼實現起來的時候,常常用
遞歸
實現,因爲樹的定義就是遞歸定義的。同時,一定要注意不要從一個點又轉移回了它的父親,否則,一定會TLE
。 - 因爲樹的點數經常是級別的,所以一定要注意
long long
。
- 基環樹是一種特殊的圖的結構。它由個點和條邊組成,不僅全圖強聯通,而且只有一個環。
- 根據樹的特點和基環樹的定義,我們可以發現基環樹就是一棵樹在加一條邊。這是基環樹很重要的一個性質。
- 所以,一般解決基環樹上問題時,我們需要找到環上一邊,如何把它斷掉,轉化爲樹來求解。
首先,像普通的樹一樣,從根到葉子訪問,訪問時記錄每個點的父親。
假設我們正在枚舉點,枚舉與u所有的相鄰的點,如果已訪問且不是的父親,那麼就是環上的一邊。
注意,這種方法只能找一條,且時間複雜度爲。
我們可以隨便指定一個頂點,從它開始,如果一個點的沒有被標記,我們先標記,如果將記爲。
不斷重複如上的算法,直到某個點的已經被標記,那麼就是環上的一邊。
Z國
的騎士團
是一個很有勢力的組織,幫會中匯聚了來自各地的精英。他們劫富濟貧,懲惡揚善,受到社會各界的讚揚。
最近發生了一件可怕的事情,邪惡的Y國
發動了一場針對Z國
的侵略戰爭。戰火綿延五百里,在和平環境中安逸了數百年的Z國
又怎能抵擋的住Y國
的軍隊。於是人們把所有的希望都寄託在了騎士團
的身上,就像期待有一個真龍天子的降生,帶領正義打敗邪惡。
騎士團
是肯定具有打敗邪惡勢力的能力的,但是騎士們互相之間往往有一些矛盾。每個騎士都有且僅有一個自己最厭惡的騎士(當然不是他自己),他是絕對不會與自己最厭惡的人一同出征的。
戰火綿延,人民生靈塗炭,組織起一個騎士軍團
加入戰鬥刻不容緩!國王
交給了你一個艱鉅的任務,從所有的騎士中選出一個騎士軍團
,使得軍團內沒有矛盾的兩人(不存在一個騎士與他最痛恨的人一同被選入騎士軍團的情況),並且,使得這支騎士軍團
最具有戰鬥力。
爲了描述戰鬥力,我們將騎士按照至編號,給每名騎士一個戰鬥力的估計,一個軍團的戰鬥力爲所有騎士的戰鬥力總和。
我們把每個騎士和他最痛恨的騎士之間連一條邊,並把他最痛恨的騎士作爲他的“父親”。不難發現,這個模型是一棵有向基環樹。
所以,我們枚舉環上的邊,把它斷開,轉化爲樹。用類似沒有上司的舞會
的方法就可以完成。
注意數組別開太多,否則任意。注意常數不要太大,否則任意。
const int N=1e6+100;
#define ll long long
struct node{
int next,to;
}e[N];int h[N],tot,n,fa[N];
inline void add(int a,int b){
e[++tot]=(node){h[a],b};h[a]=tot;
}
int profit[N];ll f[N][2],ans;bool visit[N];
void dp(int u,int fa,int root){
f[u][1]=profit[u];visit[u]=true;
for(int i=h[u];i;i=e[i].next){
register int to=e[i].to;
if (to==root) continue;
dp(to,u,root);f[u][1]+=f[to][0];
f[u][0]+=max(f[to][0],f[to][1]);
}
}
inline ll calc(int u){
memset(f,0,sizeof(f));
dp(u,-1,u);return f[u][0];
}
void find_circle(int u){
visit[u]=true;int root=u;
while (!visit[fa[root]]){
visit[fa[root]]=true;
root=fa[root];
}
ans+=max(calc(root),calc(fa[root]));
}
inline void initialization(){
memset(visit,0,sizeof(visit));
memset(profit,0,sizeof(profit));
memset(h,0,sizeof(h));ans=tot=0;
}
int main(){
// freopen("t1.in","r",stdin);
n=read();initialization();
for(int i=1;i<=n;i++){
profit[i]=read();
add(fa[i]=read(),i);
}
for(int i=1;i<=n;i++)
if (!visit[i]) find_circle(i);
printf("%lld",ans);
return 0;
}