基環樹DP
基環樹
在圖論中,樹是一種簡單圖 \(G=(V,E)\) ,其中 \(|V|=|E|+1\) ,且不存在環。
它有著這樣的一個性質:如果在 \(G\) 中任意不相連兩點間加上一條邊,新圖 \(G'=(V,E')\) 正好含有一個環。
而基環樹就是從上述性質所得到的圖 \(G'\) 。
因此,我們知道一棵基環樹是由 \(n\) 個點和 \(n\) 條邊所構成的。
如果刪掉環上的一條邊,基環樹就退化成了樹。
基環樹DP
我們常常會遇到一類動態規劃的題目是在基環樹上進行的。
對於這類題目,我們一般有以下兩種做法:
-
列舉每一條邊,判斷刪去這條邊後判斷是否變成了一棵樹,若是,則進行樹形DP。當然與這條邊相關的資訊要特殊判斷。
-
我們先從這棵基環樹上找環,對於環上的每個節點所產生的內/外向樹進行樹形DP,然後再在環上進行一次環形DP即可。
例題選講
下面提供幾道較為基礎的題目:
例題1:[ZJOI2008]騎士
\(\large{\text{Description:}}\)
有 \(n\) 個騎士組成一個隊,每個騎士有他的戰力並恨一個人。
要求相互憎恨的人不同時在隊裡,怎樣安排戰鬥力最大?
輸出最大總戰鬥力即可。
\(\large{\text{Solution:}}\)
其實這題就是 沒有上司的舞會 的基環樹版本。
我們容易發現,\(n\) 個人中會存在多個聯通塊,而每個聯通塊有且只有一個環,即原圖是個基環樹森林。
於是在每棵基環樹中,我們先找出其中的環,
對於環上的每個點 \(x\) ,我們分別對以 \(x\) 為根的子樹中進行如下的樹形DP:
設 \(dp[u][0]\) 表示在以 \(u\) 為根的這棵子樹中不選節點 \(u\) 時最大的權值和; \(dp[u][1]\) 表示在以 \(u\) 為根的這棵子樹中必選節點 \(u\) 時最大的權值和。
那麼不難得出這樣的狀態轉移方程:
\[dp[u][0]=\sum_{(u,v)\in E}\max\{dp[v][0],dp[v][1]\} \]\[dp[u][1]=\sum_{(u,v)\in E}dp[v][0]+val[u] \]接下來我們考慮在環上的操作。
在這題中,我們只需考慮其中相鄰兩個點的選取情況即可。
也就是說,對於環上的任意相鄰兩點 \(u,v\) ,我們需要強制要求其中某點不被我們選取。
將它們之間斷邊,分別跑一次樹形DP即可。
實現起來就是: \(ans=ans+\max(dp[u][0],dp[v][0])\)
至此,本題已解決。
#include<bits/stdc++.h>
#define Re register
using namespace std;
typedef long long LL;
const int N=1000005;
struct Node {
int ver,nxt;
}e[N<<1];
int cnt=1,hd[N];
int n,hte; LL a[N];
int U,V,E; LL dp[N][2],ans;
bool vis[N];
inline void add(int u,int v)
{
cnt++;
e[cnt].ver=v;
e[cnt].nxt=hd[u];
hd[u]=cnt;
}
inline void dfs1(int u,int f)
{
vis[u]=1;
for(Re int i=hd[u];i;i=e[i].nxt)
{
int v=e[i].ver;
if((i^1)==f) continue;
if(vis[v])
{
U=u,V=e[i].ver,E=i;
continue;
}
dfs1(v,i);
}
}
inline void dfs2(int u,int f)
{
dp[u][0]=0;
dp[u][1]=a[u];
for(Re int i=hd[u];i;i=e[i].nxt)
{
int v=e[i].ver;
if(i==E||(i^1)==E||(i^1)==f) continue;
dfs2(v,i);
dp[u][0]+=max(dp[v][0],dp[v][1]);
dp[u][1]+=dp[v][0];
}
}
int main()
{
scanf("%d",&n);
for(Re int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
scanf("%d",&hte);
add(hte,i);
add(i,hte);
}
for(Re int i=1;i<=n;i++)
{
if(vis[i]) continue;
dfs1(i,0);
dfs2(U,0);
LL res=dp[U][0];
dfs2(V,0);
ans+=max(res,dp[V][0]);
}
printf("%lld",ans);
return 0;
}