【题目链接】
【前置技能】
- 树形DP
【题解】
- 题意是:给出一个环套树森林,求最大独立集。
- 先考虑如何做树上的最大独立集的DP,这个问题比较基础。 f [ p o s ] [ 0 / 1 ] f[pos][0/1] f[pos][0/1]表示以 p o s pos pos为根的子树中选了/没选节点 p o s pos pos的答案。转移方程 f [ p o s ] [ 1 ] = ∑ f [ s o n ] [ 0 ] f[pos][1] = \sum f[son][0] f[pos][1]=∑f[son][0], f [ p o s ] [ 0 ] = ∑ m a x { f [ s o n ] [ 0 ] , f [ s o n ] [ 1 ] } f[pos][0] = \sum max\{f[son][0],f[son][1]\} f[pos][0]=∑max{f[son][0],f[son][1]}。
- 环套树一般来说环上有一条边是不必要的。考虑对于环上的一条边 < u , v > <u, v> <u,v>,我们将其去掉,分别以 u u u和 v v v为根做一次求树上的最大独立集的DP。因为 u , v u,v u,v之间有连边,所以答案中要么不存在 u u u,要么不存在 v v v,所以 a n s = m a x { f [ u ] [ 0 ] , f [ v ] [ 0 ] } ans = max\{f[u][0], f[v][0]\} ans=max{f[u][0],f[v][0]}。
- 时间复杂度 O ( N ) O(N) O(N)
【代码】
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define LL long long
#define MAXN 1000010
using namespace std;
struct Edg{int nxt, to;}E[MAXN << 1];
struct edg{int x, y;}e[MAXN];
int n, fa[MAXN], size[MAXN], cnt, w[MAXN], head[MAXN], CNT;
LL dp[MAXN][2], ans;
template <typename T> void chkmin(T &x, T y){x = min(x, y);}
template <typename T> void chkmax(T &x, T y){x = max(x, y);}
template <typename T> void read(T &x){
x = 0; int f = 1; char ch = getchar();
while (!isdigit(ch)) {if (ch == '-') f = -1; ch = getchar();}
while (isdigit(ch)) {x = x * 10 + ch - '0'; ch = getchar();}
x *= f;
}
int find(int x){
if (fa[x] == x) return fa[x];
else return (fa[x] = find(fa[x]));
}
int merge(int x, int y){
int fx = find(x), fy = find(y);
if (fx == fy) return 0;
if (size[fx] > size[fy]) swap(fx, fy);
fa[fx] = fy, size[fy] += size[fx];
return 1;
}
void dfs(int pos, int dad){
dp[pos][0] = 0, dp[pos][1] = w[pos];
for (int i = head[pos]; i; i = E[i].nxt){
int son = E[i].to;
if (son != dad){
dfs(son, pos);
dp[pos][0] += max(dp[son][0], dp[son][1]);
dp[pos][1] += dp[son][0];
}
}
}
void add(int u, int v){
++CNT;
E[CNT].to = v;
E[CNT].nxt = head[u];
head[u] = CNT;
}
int main(){
read(n);
for (int i = 1; i <= n; ++i)
fa[i] = i, size[i] = 1;
for (int i = 1; i <= n; ++i){
read(w[i]);
int u; read(u);
if (merge(u, i)) add(u, i), add(i, u);
else e[++cnt].x = i, e[cnt].y = u;
}
for (int i = 1; i <= cnt; ++i){
dfs(e[i].x, 0);
LL tmp = dp[e[i].x][0];
dfs(e[i].y, 0);
chkmax(tmp, dp[e[i].y][0]);
ans += tmp;
}
printf("%lld\n", ans);
return 0;
}