BZOJ1040: [ZJOI2008]骑士

【传送门:BZOJ1040


简要题意:

  给出n个人,每个人都有自己的战力值和最讨厌的人,选出若干个人使得这些人中的每一个人所讨厌的人都不在这些人中,求出最大战力值


题解:

  一看,这好像是森林,再看,还有环!

  一开始看,还不会做,结果hanks_o来D飞了我,说这道题很简单

  好吧,确实挺简单的。。

  将讨厌的人和被讨厌的人连双向边

  首先一棵一棵树的求,对于一棵树,先把环给找出来(因为题目中每个人只有一个讨厌的人,所以每棵树中最多只有一个环),然后在环中找一条边将环切断,并且切断的这条边的两点x,y不能同时选择,使得这棵树成为真正意义上的树,然后进行树形DP,对于i点,f[i][0]表示不选第i个点的最大收益,f[i][1]表示选i点的最大收益,然后对于每棵树将x拉到根处,再把y拉到根处,分别求出f[x][1],f[y][1],取最大值,这个最大值就是这棵树的最大收益

  然后把每棵树的收益都加起来就是答案

  注意:要加long long

  UPD(2018.10.5):实际上,这题是基环树森林+DP,但是当时不会,可是操作跟基环树的操作没多大差别


参考代码:

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
using namespace std;
typedef long long LL;
LL s[1100000];
struct node
{
    int x,y,next,other;
}a[2100000];int len,last[1100000];
void ins(int x,int y)
{
    int k1=++len,k2=++len;
    a[k1].x=x;a[k1].y=y;
    a[k1].next=last[x];last[x]=k1;
    a[k2].x=y;a[k2].y=x;
    a[k2].next=last[y];last[y]=k2;
    a[k1].other=k2;
    a[k2].other=k1;
}
bool v[1100000];
LL f[1100000][2];
int X,Y,K;
void bt(int x,int fa)
{
    v[x]=true;
    for(int k=last[x];k;k=a[k].next)
    {
        int y=a[k].y;
        if(y==fa) continue;
        if(v[y]==true){X=x;Y=y;K=k;continue;}
        bt(y,x);
    }
}
void dp(int x,int fa)
{
    f[x][0]=0;f[x][1]=s[x];
    for(int k=last[x];k;k=a[k].next)
    {
        int y=a[k].y;
        if(y==fa) continue;
        if(k==K||k==a[K].other) continue;
        dp(y,x);f[x][0]+=max(f[y][0],f[y][1]);f[x][1]+=f[y][0];
    }
}
int main()
{
    int n;
    scanf("%d",&n);
    len=0;memset(last,0,sizeof(last));
    for(int i=1;i<=n;i++)
    {
        int x;
        scanf("%d%d",&s[i],&x);
        ins(i,x);
    }
    memset(v,false,sizeof(v));
    LL ans=0,t1,t2;
    memset(f,0,sizeof(f));
    for(int i=1;i<=n;i++)
    {
        if(v[i]==false)
        {
            bt(i,0);
            dp(X,0);t1=f[X][0];
            dp(Y,0);t2=f[Y][0];
            ans+=max(t1,t2);
        }
    }
    printf("%lld
",ans);
    return 0;
}

 

原文地址:https://www.cnblogs.com/Never-mind/p/8384322.html