zoukankan      html  css  js  c++  java
  • BZOJ1040 [ZJOI2008]骑士

    Description

      Z国的骑士团是一个很有势力的组织,帮会中汇聚了来自各地的精英。他们劫富济贫,惩恶扬善,受到社会各
    界的赞扬。最近发生了一件可怕的事情,邪恶的Y国发动了一场针对Z国的侵略战争。战火绵延五百里,在和平环境
    中安逸了数百年的Z国又怎能抵挡的住Y国的军队。于是人们把所有的希望都寄托在了骑士团的身上,就像期待有一
    个真龙天子的降生,带领正义打败邪恶。骑士团是肯定具有打败邪恶势力的能力的,但是骑士们互相之间往往有一
    些矛盾。每个骑士都有且仅有一个自己最厌恶的骑士(当然不是他自己),他是绝对不会与自己最厌恶的人一同出
    征的。战火绵延,人民生灵涂炭,组织起一个骑士军团加入战斗刻不容缓!国王交给了你一个艰巨的任务,从所有
    的骑士中选出一个骑士军团,使得军团内没有矛盾的两人(不存在一个骑士与他最痛恨的人一同被选入骑士军团的
    情况),并且,使得这支骑士军团最具有战斗力。为了描述战斗力,我们将骑士按照1至N编号,给每名骑士一个战
    斗力的估计,一个军团的战斗力为所有骑士的战斗力总和。

    Input

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

    Output

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

    Sample Input

    3
    10 2
    20 3
    30 1

    Sample Output

    30

    HINT

    N ≤ 1 000 000,每名骑士的战斗力都是不大于 1 000 000的正整数。

    正解:基环外向树+DP

    解题报告:

      这道题的模型就是基环外向树,n个点,n条边,显然题目中给的实际上是无向边,在有重边的情况下将会成为一棵树,否则就会成为一个环,显然是若干个连通块。

      那么每个连通块可以用同样的方法处理:对于树,直接一遍树形DP即可。对于环,我们找到产生环的那条边,考虑这条边连接的两个结点,我们可以删掉这条边然后对于这条边的两个顶点u和v,第一次以u为根,做一遍树形DP,得到不取u的最大值;第二次以v为根,同样做法,两者取一个max,注意这条边必须要删掉,然而我想了点奇奇怪怪的方法才删掉的,因为有重边不是很好判...

      最后把每个连通块的答案加起来就可以了。

     1 //It is made by jump~
     2 #include <iostream>
     3 #include <cstdlib>
     4 #include <cstring>
     5 #include <cstdio>
     6 #include <cmath>
     7 #include <algorithm>
     8 #include <ctime>
     9 #include <vector>
    10 #include <queue>
    11 #include <map>
    12 #include <set>
    13 using namespace std;
    14 typedef long long LL;
    15 #define RG register
    16 const int inf = (1<<30);
    17 const int MAXN = 1000011;
    18 const int MAXM = 2000011;
    19 int n,ecnt,val[MAXN],root,root2;
    20 int first[MAXN],to[MAXM],next[MAXM];
    21 bool vis[MAXN];
    22 LL f[MAXN][2];
    23 int dui[MAXN],deep[MAXN],head,tail,father[MAXN],use[MAXN],uu;
    24 LL ans;
    25 
    26 inline int getint()
    27 {
    28     RG int w=0,q=0; RG char c=getchar();
    29     while((c<'0' || c>'9') && c!='-') c=getchar(); if(c=='-') q=1,c=getchar(); 
    30     while (c>='0' && c<='9') w=w*10+c-'0', c=getchar(); return q ? -w : w;
    31 }
    32 
    33 inline void dp(RG int x,RG int fa){
    34     f[x][0]=f[x][1]=0; use[x]=uu;
    35     for(RG int i=first[x];i;i=next[i]) {
    36     RG int v=to[i]; if(v==fa) continue;
    37     if((x==root && v==root2) || (x==root2 && v==root)) continue;//强行删掉这条边    
    38     if(use[v]==uu) continue;
    39     dp(v,x); f[x][0]+=max(f[v][0],f[v][1]); f[x][1]+=f[v][0];
    40     }
    41     f[x][1]+=val[x];
    42 }
    43 
    44 inline void find(RG int x){
    45     head=tail=0; dui[++tail]=x; RG int u;RG LL maxl=0;
    46     RG bool ok=false;
    47     while(head<tail) {
    48     head++; u=dui[head]; vis[u]=1;
    49     for(RG int i=first[u];i;i=next[i]) {
    50         RG int v=to[i]; if(v==father[u]) continue;
    51         if(deep[v]<deep[u] && deep[v]!=0) {
    52         root=u; root2=v; uu++; dp(v,0); maxl=max(maxl,f[v][0]);//强制v不选
    53         uu++; dp(u,0); maxl=max(maxl,f[u][0]);//强制u不选
    54         ok=true;
    55         continue;
    56         }
    57         deep[v]=deep[u]+1; father[v]=u; dui[++tail]=v; 
    58     }
    59     }
    60     if(ok)  ans+=maxl;
    61     else {
    62     uu++; dp(x,0);
    63     ans+=max(f[x][0],f[x][1]);
    64     }
    65 }
    66 
    67 inline void work(){
    68     n=getint();  RG int x; ecnt=1;
    69     for(RG int i=1;i<=n;i++) {
    70     val[i]=getint(); x=getint();
    71     next[++ecnt]=first[i]; first[i]=ecnt; to[ecnt]=x;
    72     next[++ecnt]=first[x]; first[x]=ecnt; to[ecnt]=i;
    73     }
    74     for(RG int i=1;i<=n;i++) if(!vis[i]) find(i);
    75     printf("%lld",ans);
    76 }
    77 
    78 int main()
    79 {
    80     work();
    81     return 0;
    82 }
  • 相关阅读:
    vm扩容
    手算二维离散傅里叶变换
    取消vim模式匹配的高亮
    DS DI ES SI等等
    int and Integer
    为什么超类能引用子类的对象
    voltile解析
    java集合
    疑问:无限定通配符
    layui table 导出
  • 原文地址:https://www.cnblogs.com/ljh2000-jump/p/5920985.html
Copyright © 2011-2022 走看看