【bzoj1040】【zjoi2008】骑士(树形dp)

题目描述 Description

Z国的骑士团是一个很有势力的组织,帮会中汇聚了来自各地的精英。他们劫富济贫,惩恶扬善,受到社会各界的赞扬。

最近发生了一件可怕的事情,邪恶的Y国发动了一场针对Z国的侵略战争。战火绵延五百里,在和平环境中安逸了数百年的Z国又怎能抵挡的住Y国的军队。于是人们把所有的希望都寄托在了骑士团的身上,就像期待有一个真龙天子的降生,带领正义打败邪恶。

骑士团是肯定具有打败邪恶势力的能力的,但是骑士们互相之间往往有一些矛盾。每个骑士都有且仅有一个自己最厌恶的骑士(当然不是他自己),他是绝对不会与自己最厌恶的人一同出征的。

战火绵延,人民生灵涂炭,组织起一个骑士军团加入战斗刻不容缓!国王交给了你一个艰巨的任务,从所有的骑士中选出一个骑士军团,使得军团内没有矛盾的两人(不存在一个骑士与他最痛恨的人一同被选入骑士军团的情况),并且,使得这支骑士军团最具有战斗力。

为了描述战斗力,我们将骑士按照1至N编号,给每名骑士一个战斗力的估计,一个军团的战斗力为所有骑士的战斗力总和。

输入描述 Input Description

第一行包含一个正整数N,描述骑士团的人数。

接下来N行,每行两个正整数,按顺序描述每一名骑士的战斗力和他最痛恨的骑士。

输出描述 Output Description

应包含一行,包含一个整数,表示你所选出的骑士军团的战斗力。

样例输入 Sample Input

3

10 2

20 3

30 1

样例输出 Sample Output

30

数据范围及提示 Data Size & Hint

对于30%的测试数据,满足N ≤ 10;

对于60%的测试数据,满足N ≤ 100;

对于80%的测试数据,满足N ≤ 10 000。

对于100%的测试数据,满足N ≤ 1 000 000,每名骑士的战斗力都是不大于 1 000 000的正整数。

题解:

可以发现如果按互相仇恨的两个人连边的话是一个环套树森林。

对于每一棵环套树

我们可以先dp所有的外向树;

dp[i][1]为以i节点为根的子树选i节点的最大值。

dp[i][0]为以i节点为根的子树不选i节点的最大值。

v[i]为节点i的战斗力。

则dp方程为:

dp[i][1]=sum(dp[j][0])(j为i的子节点);

dp[i][0]=sum(max(dp[j][1],dp[j][0]))+v[i];(j为i的子节点);

然后再任选一个位置作为链的开头破环进行dp;

f[i][0]为选第1个点,不选第i个点的最大值。

f[i][1]为选第1个点,选第i个点的最大值。

f[i][2]为不选第1个点,选第i个点的最大值。

f[i][3]为不选第1个点,不选第i个点的最大值。

c数组保存断开的那条链,c[0]为链的长度。

dp方程为:

f[i][0]=max(f[i-1][0],f[i-1][1])+dp[c[i]][0];

f[i][1]=f[i-1][0]+dp[c[i]][1];

f[i][2]=f[i-1][3]+dp[c[i]][1];

f[i][3]=max(f[i-1][3],f[i-1][2])+dp[c[i]][0];

初始值:

f[1][1]=dp[c[1]][1];

f[1][2]=f[1][3]=f[1][0]=dp[c[1]][0];

还有最后一个问题。

我们如何找出所有的环套树。

具体方法为将每个骑士讨厌的人设为他的父亲。并从他的父亲向他连边。注意只能连有向边。

然后任选一个人,向他的父亲前进。这样最后总能转到一个环上。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
struct use{
    int l,r;
}b[1000001];
int point[1000001],t,next[1000001],n,a,tot,fa[1000001],k,son[1000001],now,c[1000001];
long long dp[1000001][3],f[1000001][5],ans,v[1000001],temp;
bool mark[1000001];
void add(int x,int y)
{
    next[++tot]=point[x];point[x]=tot;
    b[tot].l=x;b[tot].r=y;
}
void init()
{
    scanf("%d",&n);
    for (int i=1;i<=n;i++)
      {
         scanf("%lld%d",&v[i],&a);
         fa[i]=a;
         add(a,i);
      }
}
void treedp(int x)
{
    dp[x][1]=v[x];
    mark[x]=true;
    for (int i=point[x];i;i=next[i])
      {
         treedp(b[i].r);
         dp[x][0]+=max(dp[b[i].r][1],dp[b[i].r][0]);
         dp[x][1]+=dp[b[i].r][0];
      }
}
void lastdp()
{
    f[1][1]=dp[c[1]][1];
    f[1][2]=f[1][3]=f[1][0]=dp[c[1]][0];
    for (int i=2;i<=c[0];i++)
      {
          f[i][0]=max(f[i-1][0],f[i-1][1])+dp[c[i]][0];
          f[i][1]=f[i-1][0]+dp[c[i]][1];
          f[i][2]=f[i-1][3]+dp[c[i]][1];
          f[i][3]=max(f[i-1][3],f[i-1][2])+dp[c[i]][0];
      }
     temp=max(f[c[0]][0],max(f[c[0]][2],f[c[0]][3]));
}
void solve()
{
    for (int i=1;i<=n;i++)
      {

          if (mark[i]) continue;
          k=i;
          c[0]=0;
          while (!mark[k])
          {
             mark[k]=true;
             k=fa[k];
             son[fa[k]]=k;
          }
          now=k;
         while (1)
           {
              t=point[k];
              dp[k][1]=v[k];
              while (t>0)
               {
                if (b[t].r!=son[k])
                  {
                  treedp(b[t].r);
                  dp[k][0]+=max(dp[b[t].r][1],dp[b[t].r][0]);
                  dp[k][1]+=dp[b[t].r][0];
                  }
                 t=next[t];
               }
               c[++c[0]]=k;
               k=fa[k];
               if (k==now) break;
          }
          lastdp();
          ans+=temp;
      }
    printf("%lld",ans);
}
int main()
{
    init();
    solve();
    return 0;
}
时间: 2024-10-25 22:53:10

【bzoj1040】【zjoi2008】骑士(树形dp)的相关文章

【BZOJ1040】[ZJOI2008]骑士 树形DP

[BZOJ1040][ZJOI2008]骑士 Description Z国的骑士团是一个很有势力的组织,帮会中汇聚了来自各地的精英.他们劫富济贫,惩恶扬善,受到社会各界的赞扬.最近发生了一件可怕的事情,邪恶的Y国发动了一场针对Z国的侵略战争.战火绵延五百里,在和平环境中安逸了数百年的Z国又怎能抵挡的住Y国的军队.于是人们把所有的希望都寄托在了骑士团的身上,就像期待有一个真龙天子的降生,带领正义打败邪恶.骑士团是肯定具有打败邪恶势力的能力的,但是骑士们互相之间往往有一些矛盾.每个骑士都有且仅有一个

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

这是一个森林中, 每棵树上都有一个环...每棵树单独处理, 找出环上任意一条边断开, 限制一下这条边两端点的情况, 然后就可以树dp了.. ------------------------------------------------------------------------ #include<cstdio> #include<algorithm> #include<cstring> #include<cctype> using namespace

【环套树+树形dp】Bzoj1040 [ZJOI2008] 骑士

Description Z国的骑士团是一个很有势力的组织,帮会中汇聚了来自各地的精英.他们劫富济贫,惩恶扬善,受到社会各界的赞扬.最近发生了一件可怕的事情,邪恶的Y国发动了一场针对Z国的侵略战争.战火绵延五百里,在和平环境中安逸了数百年的Z国又怎能抵挡的住Y国的军队.于是人们把所有的希望都寄托在了骑士团的身上,就像期待有一个真龙天子的降生,带领正义打败邪恶.骑士团是肯定具有打败邪恶势力的能力的,但是骑士们互相之间往往有一些矛盾.每个骑士都有且仅有一个自己最厌恶的骑士(当然不是他自己),他是绝对不

【bzoj1040】骑士[ZJOI2008](树形dp)

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=1040 这道题,很明显根据仇恨关系构造出的图形是一堆环套树.如果是普通的树,就可以马上裸树形dp了,于是我们先解决这个环的问题.所以要先把环找出来. 找出环后,假如断掉一条环边,环套树就变成了普通的树,而我们可以直接对这棵树进行dp,但是要控制被断掉的边的两端不被选择. 对断边形成的树进行dp的时候,我们的dp方程是这样表示的:f[i][0/1]表示结点i不选/选. 假设断掉的两条边两端

【ZJOI2008】骑士[树形dp]

[ZJOI2008]骑士 很容易就能想到将一个骑士不喜欢的骑士设为他的父亲 每一个骑士只有一个讨厌的人 那么它的入度只能为1 所以对于每个连通块 它一定有且只有一个包含根节点的环 所以将环拆开 第一遍dp为不选它的父亲 第二遍dp为不选它的父亲的父亲 (其实我也不太明白为啥这么搞) #include<bits/stdc++.h> using namespace std; #define ll long long #define rg register #define Max(x,y) ((x)

[BZOJ1040][ZJOI2008]骑士(环套树dp)

1040: [ZJOI2008]骑士 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 5816  Solved: 2263[Submit][Status][Discuss] Description Z国的骑士团是一个很有势力的组织,帮会中汇聚了来自各地的精英.他们劫富济贫,惩恶扬善,受到社会各 界的赞扬.最近发生了一件可怕的事情,邪恶的Y国发动了一场针对Z国的侵略战争.战火绵延五百里,在和平环境 中安逸了数百年的Z国又怎能抵挡的住Y国的军队.于是人

Bzoj1040 [ZJOI2008]骑士

Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 4496  Solved: 1721 Description Z国的骑士团是一个很有势力的组织,帮会中汇聚了来自各地的精英.他们劫富济贫,惩恶扬善,受到社会各界的赞扬.最近发生了一件可怕的事情,邪恶的Y国发动了一场针对Z国的侵略战争.战火绵延五百里,在和平环境中安逸了数百年的Z国又怎能抵挡的住Y国的军队.于是人们把所有的希望都寄托在了骑士团的身上,就像期待有一个真龙天子的降生,带领正义打败邪恶.骑士团

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

N个点,每个点发出一条边,那么这个图的形状一定是一个基环树森林(如果有重边就会出现森林) 那我做f[0][x]和f[1][x]分别表示对于x子树,x这个点选还是不选所带来的最大价值 然后就变成了这好几个环上不能选相邻的点,最大的价值和 我们把这个环从N到1处断开,然后钦定一下1选还是不选,统计一下答案就可以了. 1 #include<bits/stdc++.h> 2 #define pa pair<int,int> 3 #define ll long long 4 using na

$bzoj1040-ZJOI2008$ 骑士 树形$dp$

题面描述 \(Z\)国的骑士团是一个很有势力的组织,帮会中汇聚了来自各地的精英.他们劫富济贫,惩恶扬善,受到社会各界的赞扬.最近发生了一件可怕的事情,邪恶的Y国发动了一场针对\(Z\)国的侵略战争.战火绵延五百里,在和平环境中安逸了数百年的\(Z\)国又怎能抵挡的住\(Y\)国的军队.于是人们把所有的希望都寄托在了骑士团的身上,就像期待有一个真龙天子的降生,带领正义打败邪恶.骑士团是肯定具有打败邪恶势力的能力的,但是骑士们互相之间往往有一些矛盾.每个骑士都有且仅有一个自己最厌恶的骑士(当然不是他

bzoj1040(ZJOI2008)骑士——基环树

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1040 基环树的模板. 套路就是把环断开,先把一端作为根节点,强制不选:再把另一端作为根节点,强制不选. 人家的这个判断环的方法真好!还顺便没有连上环的那条边,省下了在函数里判断. 别忘了有多棵基环树! #include<iostream> #include<cstdio> #include<cstring> #define ll long long using n