[bzoj1040]: [ZJOI2008]骑士

http://www.lydsy.com/JudgeOnline/problem.php?id=1040

题意:有n个骑士,每个骑士都有一个憎恶的人不能和他同时选,每个骑士有一个战斗力,求最大的战斗力的和

n<=1,000,000   数据范围有点吓人

很明显这是一个无向环带外向树的图。

把环找出来,然后断掉其中的一条边(这样就成了树),分别以这条边的两头为根,并且强制不选另一头,用树形dp算一下答案,取最大值即可。

f[i][0] 表示点i不选的最大战斗力,f[i][1]表示点i选了的战斗力

f[i][1]=sigma(f[v][0]) v是i的儿子

f[i][0]=sigma(max(f[v][0],f[v][1]))

#include<iostream>
#include<cstdio>
#define ll long long
#define MAXN 1000000
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0'; ch=getchar();}
    return x*f;
}

int s[MAXN+5];
ll f[MAXN+5][2];
int n,rt1,rt2,cnt=1;
ll ans=0,maxn;
struct edge{
    int to,next;
}e[MAXN*2+5];
int head[MAXN+5];
bool b[MAXN+5];
bool vis[MAXN+5];

inline void ins(int f,int t)
{
    e[++cnt].next=head[f];
    head[f]=cnt;
    e[cnt].to=t;
}

void dfs(int x,int fa)
{
    b[x]=1;
    for(int i=head[x];i&&!rt1;i=e[i].next)
    {
        int v=e[i].to;
        if(v!=fa)
        {
            if(b[v]) {rt1=x;rt2=v;e[i].to=e[i^1].to=-1;return;}
            dfs(v,x);
        }
    }
}

void dp(int x,int fa,int ban)
{
    b[x]=1;if(x!=ban)f[x][1]=s[x];else f[x][1]=0;f[x][0]=0;
    for(int i=head[x];i;i=e[i].next)
    {
        int v=e[i].to;
        if(v!=-1&&v!=fa)
        {
            dp(v,x,ban);
            f[x][1]+=f[v][0];
            f[x][0]+=max(f[v][1],f[v][0]);
        }
    }
}

int main()
{
    n=read();
    for(int i=1;i<=n;i++)
    {
        s[i]=read();int x=read();
        ins(i,x);ins(x,i);
    }
    for(int i=1;i<=n;i++)
        if(!b[i])
        {
            rt1=rt2=0;dfs(i,0);
            dp(rt1,0,rt2);
            maxn=max(f[rt1][1],f[rt1][0]);
            dp(rt2,0,rt1);
            maxn=max(maxn,max(f[rt2][1],f[rt2][0]));
            ans+=maxn;
        }
    cout<<ans;
    return 0;
}

 

posted @ 2017-02-24 23:49  FallDream  阅读(234)  评论(0编辑  收藏  举报