关闭

【bzoj1040】骑士 基环外向树 树规

标签: 基环外向树树形动规bzoj
456人阅读 评论(0) 收藏 举报
分类:

题目描述

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

输入描述

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

输出描述

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

样例输入

3
10 2
20 3
30 1

样例输出

30

数据范围

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

这个题是一个比较复杂的树规的题,实际上就是多颗基环外向树上dp出最大值,然后加在一起
我这里先解释一下为什么是基环外向树以及基环外向树是什么
每个骑士只有一位痛恨的骑士,如果把他们两个连起来那么一个有n个节点的图就只有n条边,所以一定是有环的,如果把这个环看成根节点,那么整个就是一棵树。
然后下面我们来考虑怎么来dp每一颗基环外向树,我们先找到树上的环(一定会有环),然后可以把环上的每一个节点当成根节点,它就会产生一颗子树,这个子树就是一颗普通的子树,跑一次普通的树规就可以跑出来
方程式还是给一下:
f[i][0]=max(f[soni][1],f[soni][0])//不取i节点的最大值
f[i][1]=f[soni][0]//取i节点的最大值
然后每个节点就有两个值了,一个取这个节点,整棵子树的最大值,一个不取的。
然后从环上任意一点开始做一次环形不能取相邻点的dp
这个就建立一个f[i][0/1][0/1]表示第i个节点去不去自己与去不去第一个
方程式差不多:
F[1][1][fa]=F[1][0][i]+dp[1][fa]
F[1][0][fa]=max(dpr[1][0][i],F[1][1][i])+f[0][fa]
F[0][1][fa]=F[0][0][i]+dp[1][fa]
F[0][0][fa]=max(F[0][0][i],F[0][1][i])+f[0][fa]
i节点是fa的上一个节点,然后最后找倒数第二个的取第一个的两个的最大值和最后一个数的不取第一个数的两个最大值,在这四个值里面取最大的一个就是我们需要的最大值
然后再把全部加起来就可以了
程序有一点长,但还是比较好写,代码见下

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<set>
#include<queue>
#include<algorithm>
#include<vector>
#include<cstdlib>
#include<cmath>
#include<ctime>
#include<stack>
#define INF 2100000000
#define clr(x)  memset(x,0,sizeof(x))
#define ll long long
#define M 1000005

using namespace std;

struct tree2
{
    int n,fa;
    vector<int>son;
}tree[M];

ll n,h[M],b[M],dp[2][M],dpr[2][2][M],B[M],rnd[M];
ll ans;

int get_round(int x)
{
    clr(b);
    while(tree[x].fa)
    {
        if(b[x])return x;
        b[x]=1;
        x=tree[x].fa;
    }
    return 0;
}


ll work1(int x)
{
    B[x]=1;
    dp[1][x]=h[x];
    dp[0][x]=0;
    for(int i=0;i<tree[x].son.size();i++)
    {
        int v=tree[x].son[i];
        if(!rnd[v])
        {
            work1(v);
            dp[1][x]+=dp[0][v];
            dp[0][x]+=max(dp[0][v],dp[1][v]);
        }

    }
    return max(dp[1][x],dp[0][x]);
}

ll work2(int x)
{
    clr(b);
    int r=get_round(x);
    clr(b);
    clr(rnd);
    int pos=r;
    while(!rnd[pos])
    {
        rnd[pos]=1;
        pos=tree[pos].fa;
    }
    pos=r;
    while(!b[pos])
    {
        work1(pos);
        b[pos]=1;
        pos=tree[pos].fa;
    }
    dpr[1][1][r]=dp[1][r];
    dpr[1][0][r]=dpr[0][0][r]=dpr[0][1][r]=dpr[0][0][r]=dp[0][r];
    clr(b);
    pos=r;
    while(tree[pos].fa!=r)
    {
        int f=tree[pos].fa;
        dpr[1][1][f]=dpr[1][0][pos]+dp[1][f];
        dpr[1][0][f]=max(dpr[1][0][pos],dpr[1][1][pos])+dp[0][f];
        dpr[0][1][f]=dpr[0][0][pos]+dp[1][f];
        dpr[0][0][f]=max(dpr[0][0][pos],dpr[0][1][pos])+dp[0][f];
        pos=f;
    }
    return max(dpr[1][0][pos],max(dpr[0][0][pos],dpr[0][1][pos]));
}

int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        int x;
        scanf("%d%d",&h[i],&x);
        tree[x].son.push_back(i);
        tree[i].fa=x;
    }

    for(int i=1;i<=n;i++)
        if(!B[i])
        {
            clr(b);
            ans+=work2(i);
        }
    cout<<ans;

    return 0;
}

大概就是这个样子,如果有什么问题,或错误,请在评论区提出,谢谢。

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:16863次
    • 积分:942
    • 等级:
    • 排名:千里之外
    • 原创:77篇
    • 转载:1篇
    • 译文:0篇
    • 评论:28条
    最新评论