SP5971 LCMSUM - LCM Sum
阿新 • • 發佈:2020-09-09
P2607 [ZJOI2008]騎士
題目連結
基環樹DP。
我們可以把\(x\)的仇人\(y\)向\(x\)連一條邊,這樣會形成好多聯通塊,每個聯通塊上有個基環樹。
對於基環樹的題,大體思路都是斷掉環上的一條邊,把它當成樹來做。
假設現在已經斷掉了一條邊,那麼轉移方程就是:\(f[x][0] += max(f[y][1], f[y][0]); \ f[x][1] += f[y][0];\)(樹形DP)
對於每個聯通塊上面的環,我們斷掉環上的任意一條邊得到的結果都是一樣的。
假設我們現在斷掉一條邊連結\(x, y\)兩個點,我們分兩種情況,強制選\(x\)不選\(y\),強制選\(y\)
#include <bits/stdc++.h> using namespace std; inline long long read() { long long s = 0, f = 1; char ch; while(!isdigit(ch = getchar())) (ch == '-') && (f = -f); for(s = ch ^ 48;isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48)); return s * f; } const int N = 1e6 + 5; int n, cnt, root; int fa[N], vis[N], val[N], head[N]; long long ans, f[N][2]; struct edge { int to, nxt; } e[N]; void add(int x, int y) { e[++cnt].nxt = head[x]; head[x] = cnt; e[cnt].to = y; } void DP(int x) { vis[x] = 1; f[x][0] = 0; f[x][1] = val[x]; for(int i = head[x]; i ; i = e[i].nxt) { int y = e[i].to; if(y != root) { DP(y); f[x][0] += max(f[y][1], f[y][0]); f[x][1] += f[y][0]; } } } void work(int x) { vis[root = x] = 1; while(!vis[fa[root]]) root = fa[root], vis[root] = 1; DP(root); long long tmp = f[root][0]; DP(root = fa[root]); ans += max(tmp, f[root][0]); } int main() { n = read(); for(int i = 1, x; i <= n; ++ i) { val[i] = read(); fa[i] = x = read(); add(x, i); } for(int i = 1;i <= n; i++) if(!vis[i]) work(i); printf("%lld", ans); return 0; }