树型打牌:洛谷P2607
这道题目一开始没有想到解法,只是想到没有上司的舞会,觉得十分的类似呀。
之后发现:n个点,n条边,只要删去一条边,就变成了和上题一模一样的做法。
那么考虑删去的这条边,实际上是解除了两个点之间的限制关系。所以我们只需要分别以他们为根,求出在不取它的情况下所能获得的最大值。
这是因为这两种方案显然只能取其一(这两个点不能同时取)。
dp[u][0/1]代表是否取当前点的最大值(它&它的子树)。
#include <bits/stdc++.h> using namespace std; #define maxn 1000050 #define int long long int n, ans, ath, dep[maxn], mark[maxn], s, cnp = 1, aim, head[maxn], v[maxn], opp[maxn], degree[maxn], dp[maxn][2]; bool vis[maxn]; struct edge { int to, last; }E[maxn]; int read() { int x = 0; char c; c = getchar(); while(c < ‘0‘ || c > ‘9‘) c = getchar(); while(c >= ‘0‘ && c <= ‘9‘) x = x * 10 + c - ‘0‘, c = getchar(); return x; } void add(int u, int v) { E[cnp].to = v; E[cnp].last = head[u]; head[u] = cnp ++; } void init() { memset(dp, 0, sizeof(dp)); memset(dep, 0, sizeof(dep)); } void search(int u) { vis[u] = true; for(int i = head[u]; i; i = E[i].last) { int v = E[i].to; if(dep[v]) continue; dep[v] = dep[u] + 1; search(v); dp[u][0] += max(dp[v][1], dp[v][0]); dp[u][1] += dp[v][0]; } dp[u][1] += v[u]; } void dfs(int u, int fa) { dep[u] = dep[fa] + 1; for(int i = head[u]; i; i = E[i].last) { int v = E[i].to; if(dep[v]) { if(v != fa) aim = u, ath = v; continue; } dfs(v, u); } } signed main() { n = read(); for(int i = 1; i <= n; i ++) { v[i] = read(), opp[i] = read(); degree[opp[i]] ++; if(i == opp[opp[i]]) continue; add(opp[i], i); add(i, opp[i]); } for(int i = 1; i <= n; i ++) { if(vis[i]) continue; init(); ath = aim = 0; dfs(i, 0); init(); if(aim) { dep[aim] = 1; search(aim); int tem = dp[aim][0]; init(); dep[ath] = 1; search(ath); tem = max(tem, dp[ath][0]); ans += tem; } else { dep[i] = 1; search(i); ans += max(dp[i][0], dp[i][1]); } } printf("%lld\n", ans); return 0; }