传送门:http://www.lydsy.com:808/JudgeOnline/problem.php?id=1040
n个点,n条边的无向图,求最大权和独立集,乍看之下不可做,但实际本题有一个特殊的性质,即一个骑士只有一个他最痛恨的人,
这样就表明了图中的环没有公共点,那么我们就可以进行环套树dp,将以环上每个点为根的树dp找出最优值,再把环拆成链,记录
链首是否选择,就可以了。
#include <iostream> #include <cstdio> #include <cstring> #include <list> #include <queue> using namespace std; typedef long long LL; const int maxn = 1000010; list <int> edge[maxn]; int a[maxn], cnt[maxn]; LL f[maxn][2], g[maxn][2][2]; bool vis[maxn]; int n; LL ans; queue <int> q; void get(int &tmp) { tmp = 0; char ch = getchar(); for(; ch < '0' || ch > '9'; ch = getchar()); for(; ch >= '0' && ch <= '9'; ch = getchar()) tmp = tmp * 10 + ch - '0'; return; } void dp(int x) { f[x][1] += (LL)a[x]; g[x][0][0] = f[x][0]; g[x][1][1] = f[x][1]; int nxt = 0; for(list <int> :: iterator p = edge[x].begin(); p != edge[x].end(); p ++) { if(!vis[*p]) { nxt = *p; break; } } while(nxt) { vis[x] = true; f[nxt][1] += (LL)a[nxt]; g[nxt][0][0] = max(g[x][0][0], g[x][1][0]) + f[nxt][0]; g[nxt][1][0] = g[x][0][0] + f[nxt][1]; g[nxt][0][1] = max(g[x][0][1], g[x][1][1]) + f[nxt][0]; g[nxt][1][1] = g[x][0][1] + f[nxt][1]; int tmp = 0; for(list <int> :: iterator p = edge[nxt].begin(); p != edge[nxt].end(); p ++) { if(!vis[*p]) { tmp = *p; break; } } x = nxt; nxt = tmp; } vis[x] = true; ans += max(g[x][0][0], max(g[x][1][0], g[x][0][1])); return; } int main() { get(n); int v; for(int i = 1; i <= n; i ++) { get(a[i]); get(v); edge[i].push_back(v); edge[v].push_back(i); cnt[i] ++; cnt[v] ++; } for(int i = 1; i <= n; i ++) { if(cnt[i] == 1) q.push(i); } while(!q.empty()) { int t = q.front(); q.pop(); f[t][1] += (LL)a[t]; for(list <int> :: iterator p = edge[t].begin(); p != edge[t].end(); p ++) { if(!vis[*p]) { f[*p][0] += max(f[t][0], f[t][1]); f[*p][1] += f[t][0]; cnt[*p] --; if(cnt[*p] == 1) q.push(*p); } } vis[t] = true; } for(int i = 1; i <= n; i ++) { if(!vis[i]) dp(i); } cout << ans << endl; return 0; }