版权声明:本文为hzy原创文章,未经博主允许不可随意转载。 https://blog.csdn.net/Binary_Heap/article/details/82082778
题意
求由基环树组成的森林的最大权独立集,
题解
对于每个连通块,把它们的最大独立集权值加起来。
对于一个连通块(基环树),考虑如何求最大独立集
假如在一棵树上,它就是个简单的树形dp:
和 分别表示在以结点 为根的子树内, 选/不选的最大独立集的权值
转移: ,
那对于这题的基环树,只需在环上任意断开,假设在 断开,就以u、v为根各自树形dp,这个基环树的最大独立集权值就是 (如果u/v取了,就无法保证它们一定没有冲突)
注意一点,判断边需要用编号而不能用两个点,因为存在大小为2的环,也就是有两条无向边,实际上我们只断开了一条.
#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
using namespace std;
typedef long long LL;
const int MAXN = 1e6 + 10;
struct Edge {
int to, nxt;
} e[MAXN << 1];
int hd[MAXN], num;
void Add(int u, int v) {
e[num].to = v;
e[num].nxt = hd[u];
hd[u] = num ++; //e下标从0开始,方便判断反向边
}
int fa[MAXN], val[MAXN], n;
int dfn[MAXN], idx;
int d_id, d_u, d_v; //断开的边
void get_loop(int u) {
dfn[u] = ++ idx;
for (int i = hd[u]; ~i; i = e[i].nxt) {
int v = e[i].to;
if(v == fa[u]) continue ;
if(dfn[v]) {
if(dfn[v] < dfn[u]) continue ;
d_id = i, d_u = u, d_v = v;
} else fa[v] = u, get_loop(v);
}
}
bool vis[MAXN];
LL g[MAXN][2];
void dp(int u, int f) {
g[u][0] = 0;
g[u][1] = val[u];
for (int i = hd[u]; ~i; i = e[i].nxt) {
int v = e[i].to;
if(d_id == i || (i ^ 1) == d_id) continue ;
if(v == f) continue ;
dp(v, u);
g[u][0] += max(g[v][0], g[v][1]);
g[u][1] += g[v][0];
}
}
int main() {
memset(hd, -1, sizeof hd);
scanf("%d", &n);
for(int i = 1, f; i <= n; i ++) {
scanf("%d%d", &val[i], &f);
Add(f, i), Add(i, f);
}
LL ans = 0, ans1, ans2;
for(int i = 1; i <= n; i ++)
if(!dfn[i]) {
get_loop(i);
dp(d_u, 0); ans1 = g[d_u][0];
dp(d_v, 0); ans2 = g[d_v][0];
ans += max(ans1, ans2);
}
printf("%lld\n", ans);
return 0;
}