【树形DP】BZOJ1040-[ZJOI2008]骑士
【题目大意】
有n个骑士,给出他们的能力值和最痛恨的一位骑士。选出一个骑士军团,使得军团内没有矛盾的两人(不存在一个骑士与他最痛恨的人一同被选入骑士军团的情况),并且,使得这支骑士军团最具有战斗力,求战斗力的最大值。
【思路】
首先yy一下,可以知道这是一个基环森林。我们可以用以下方法:
首先在每一棵基环树的环上任意找到一条边(用dfs来实现),记它的两个端点为u和v。然后删掉这条边(我这里用的方法是记录u,v在对方容器中的位置,并在后续操作中忽略这条边)。由于u和v不能同时取,在删掉u和v之间的边后存在以下两种情况:
令g[i]为不取i时i及其子树的最大战斗力总和,f[i]则表示取i。
(1)u不取,v任意。则以u为根进行树形DP,结果为g[u];
(2)v不取,u任意。则以v为根进行树形DP,结果为g[v]。
然后ans+max(g[u],g[v])。
树形DP的过程非常地常规,就不赘述了 。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<vector> 6 using namespace std; 7 const int MAXN=1000000+500; 8 typedef long long ll; 9 vector<int> E[MAXN]; 10 int n,power[MAXN],hate[MAXN]; 11 int vis[MAXN]; 12 int U,V; 13 ll g[MAXN],f[MAXN]; 14 15 void addedge(int u,int v) 16 { 17 E[u].push_back(v); 18 E[v].push_back(u); 19 } 20 21 void dfs(int u,int fr) 22 { 23 vis[u]=1; 24 for (int i=0;i<E[u].size();i++) 25 { 26 int to=E[u][i]; 27 if (to!=fr) 28 { 29 if (!vis[to]) dfs(to,u); 30 else 31 { 32 vis[to]=1; 33 U=u;V=to; 34 return; 35 } 36 } 37 } 38 } 39 40 void TreeDP(int u,int fr,int rt,int ban) 41 { 42 vis[u]=1; 43 f[u]=power[u]; 44 g[u]=0; 45 for (int i=0;i<E[u].size();i++) 46 { 47 int to=E[u][i]; 48 if (u==rt && i==ban) continue; 49 if (to!=fr && to!=rt) 50 { 51 TreeDP(to,u,rt,ban); 52 f[u]+=g[to]; 53 g[u]+=max(g[to],f[to]); 54 } 55 } 56 } 57 58 void init() 59 { 60 scanf("%d",&n); 61 for (int i=1;i<=n;i++) 62 { 63 scanf("%d%d",&power[i],&hate[i]); 64 addedge(i,hate[i]); 65 } 66 } 67 68 void get_ans() 69 { 70 memset(vis,0,sizeof(vis)); 71 ll ans=0; 72 for (int i=1;i<=n;i++) 73 if (!vis[i]) 74 { 75 dfs(i,-1); 76 int banu,banv; 77 for (int i=0;i<E[U].size();i++) if (E[U][i]==V) 78 { 79 banu=i; 80 break; 81 } 82 for (int i=0;i<E[V].size();i++) if (E[V][i]==U) 83 { 84 banv=i; 85 break; 86 } 87 88 89 TreeDP(U,-1,U,banu); 90 ll uans=g[U]; 91 92 TreeDP(V,-1,V,banv); 93 ll vans=g[V]; 94 ans+=max(uans,vans); 95 } 96 cout<<ans<<endl; 97 } 98 99 int main() 100 { 101 init(); 102 get_ans(); 103 return 0; 104 }