AmazingCounters.com

[BZOJ]1040: [ZJOI2008]骑士

题目大意:n个骑士,每人有一个权值和一个讨厌的人,要求选出若干个骑士,每个人都不能和他讨厌的人被一起选,求选出的最大权值。(n<=10^6)

思路:每人向他讨厌的人连边,得到若干棵基环外向树,每棵找到环上一点枚举选不选把环拆开树形DP即可,复杂度O(n)。

#include<cstdio>
#include<algorithm>
using namespace std;
#define ll long long
inline int read()
{
    int x;char c;
    while((c=getchar())<'0'||c>'9');
    for(x=c-'0';(c=getchar())>='0'&&c<='9';)x=(x<<3)+(x<<1)+c-'0';
    return x;
}
#define MN 1000000
struct edge{int nx,t;}e[MN*2+5];
int h[MN+5],en=1,w[MN+5],u[MN+5],p,lv;
ll f[MN+5][2];
inline void ins(int x,int y)
{
    e[++en]=(edge){h[x],y};h[x]=en;
    e[++en]=(edge){h[y],x};h[y]=en;
}
void dfs(int x,int f)
{
    u[x]=1;
    for(int i=h[x];i;i=e[i].nx)if(i!=f)
        if(u[e[i].t])p=e[i].t;
        else dfs(e[i].t,i^1);
}
void dp(int x)
{
    u[x]=lv;f[x][0]=0;f[x][1]=w[x];
    for(int i=h[x];i;i=e[i].nx)if(u[e[i].t]<lv)
    {
        dp(e[i].t);
        f[x][0]+=max(f[e[i].t][0],f[e[i].t][1]);
        f[x][1]+=f[e[i].t][0];
    }
}
int main()
{
    int n=read(),i,j;ll s1,s2,ans=0;
    for(i=1;i<=n;++i)w[i]=read(),ins(i,read());
    for(i=1;i<=n;++i)if(!u[i])
    {
        dfs(p=i,0);u[p]=3;
        for(lv=2,s1=0,j=h[p];j;j=e[j].nx)if(u[e[j].t]<lv)
        {
            dp(e[j].t);
            s1+=max(f[e[j].t][0],f[e[j].t][1]);
        }
        for(j=h[p];j;j=e[j].nx)w[e[j].t]=0;
        for(lv=3,s2=w[p],j=h[p];j;j=e[j].nx)if(u[e[j].t]<lv)
        {
            dp(e[j].t);
            s2+=f[e[j].t][0];
        }
        ans+=max(s1,s2);
    }
    printf("%lld",ans);
}

 

posted on 2017-03-31 13:42  ditoly  阅读(152)  评论(0编辑  收藏  举报