基环树/树形DPBZOJ1040-[ZJOI2008]骑士

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基环树/树形DPBZOJ1040-[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 } 

 

以上是关于基环树/树形DPBZOJ1040-[ZJOI2008]骑士的主要内容,如果未能解决你的问题,请参考以下文章

bzoj1040: [ZJOI2008]骑士(基环树+树形dp)

ZJOI2008骑士(Bzoj1040)——树形DP(基环树)

bzoj 1040: [ZJOI2008]骑士基环树+树形dp

bzoj1040(ZJOI2008)骑士——基环树

luogu2607/bzoj1040 [ZJOI2008]骑士 (基环树形dp)

BZOJ 1040: [ZJOI2008]骑士(基环树dp)