BZOJ 1040 ZJOI 2008 骑士 树形DP
题意:
有一些战士,他们有战斗力和讨厌的人,选择一些战士,使他们互不讨厌,且战斗力最大,范围1e6
分析:
把战士看作点,讨厌的关系看作一条边,连出来的是一个基环树森林。
对于一棵基环树,我们找出环,选择环上一条边(u,v)。
那么只需考虑两种情况:1、u不选,v任意;2、v不选,u任意。答案取max累计即可
程序:
1 #include <cstdio> 2 #include <cstdlib> 3 #include <cstring> 4 #include <string> 5 #include <algorithm> 6 #include <iostream> 7 8 using namespace std; 9 10 #define REP(i, a, b) for (int i = (a), i##_end_ = (b); i <= i##_end_; ++i) 11 #define mset(a, b) memset(a, b, sizeof(a)) 12 #define max_(a, b) a > b ? a : b 13 const int maxn = 1e6+10; 14 typedef long long LL; 15 int n; 16 struct Edge 17 { 18 int v, nxt; 19 Edge (int v = 0, int nxt = 0): 20 v(v), nxt(nxt) {} 21 }e[maxn*2]; 22 int head[maxn], label; 23 int U, V, E, w[maxn]; 24 bool vis[maxn]; 25 LL f[maxn][2]; 26 27 template <class TAT> 28 void Ckmax(TAT &a, const TAT &b) 29 { 30 if (a < b) a = b; 31 } 32 33 void ins(int u, int v) 34 { 35 e[++label] = Edge(v, head[u]), head[u] = label; 36 e[++label] = Edge(u, head[v]), head[v] = label; 37 } 38 39 void dfs(int u, int fa) 40 { 41 for (int i = head[u]; i != -1; i = e[i].nxt) 42 { 43 int v = e[i].v; 44 if (v == fa) continue ; 45 if (vis[v]) 46 { 47 U = u, V = v, E = i; 48 continue ; 49 } 50 vis[v] = true; 51 dfs(v, u); 52 } 53 } 54 55 void work(int u, int fa, int ban) 56 { 57 f[u][0] = 0, f[u][1] = w[u]; 58 for (int i = head[u]; i != -1; i = e[i].nxt) 59 { 60 int v = e[i].v; 61 if (v == fa || i == ban || i == (ban^1)) continue ; 62 work(v, u, ban); 63 f[u][0] += max_(f[v][0], f[v][1]); 64 f[u][1] += f[v][0]; 65 } 66 } 67 68 int main() 69 { 70 scanf("%d", &n); 71 REP(i, 1, n) head[i] = -1; 72 label = -1; 73 REP(i, 1, n) 74 { 75 int v; 76 scanf("%d %d", &w[i], &v); 77 ins(i, v); 78 } 79 mset(vis, 0), mset(f, 0); 80 LL ans = 0; 81 REP(i, 1, n) 82 if (!vis[i]) 83 { 84 vis[i] = true, dfs(i, 0); 85 LL temp; 86 work(U, 0, E); 87 temp = f[U][0]; 88 work(V, 0, E); 89 Ckmax(temp, f[V][0]); 90 ans += temp; 91 } 92 printf("%lld\n", ans); 93 return 0; 94 }
Nothing is impossible!