BZOJ1040-[ZJOI2008]骑士


题解:
题意:有一个基环树森林,每个点有权值,取出互不相邻的任意个点,问最大权值和是多少。
先考虑是一棵树的情况:
d p [ i ] [ 0 / 1 ] 表示以 i 为根的子树中 i ( 1 ) 或不取 ( 0 ) 的最大权值和
d p [ u ] [ 0 ] = m a x ( d p [ v ] [ 0 ] , d p [ v ] [ 1 ] )
d p [ u ] [ 1 ] = a [ u ] + d p [ v ] [ 0 ]
那加一条边变成了环之后呢?
取出环上相邻的两个点 x y ,将连边断开,变成一棵树,而这两个点不能同时取。
分别以这两个点为根做树形 d p
a n s = m a x ( d p 1 [ x ] [ 0 ] , d p 2 [ y ] [ 0 ] )
C o d e

#include<bits/stdc++.h>
#define N 2000005
#define ll long long
using namespace std;
int tot=-1,rr,rl,head[N],s[N],n,vis[N],flag;
ll f[N][2],ans,a[N];
struct node
{
    int next,vet;
}edge[N];
void add(int u,int v)
{
    edge[++tot].vet=v;
    edge[tot].next=head[u];
    head[u]=tot;
}
void dfs(int u,int fa)
{
    vis[u]=true;
    for(int i=head[u];i!=-1&&(!flag);i=edge[i].next)
    {
        int v=edge[i].vet;
        if(v!=fa)
        {
            if(vis[v])
            {
                rl=u;
                rr=v;
                s[i]=s[i^1]=-1;
                flag=true;
                break;
            }
            dfs(v,u);
        }
    }
}
void dp(int u,int fa,int ban)
{
    vis[u]=true;
    if(u!=ban)f[u][1]=a[u];else f[u][1]=0;
    f[u][0]=0;
    for(int i=head[u];i!=-1;i=edge[i].next)
    {
        int v=edge[i].vet;
        if(v!=fa&&s[i]!=-1)
        {
            dp(v,u,ban);
            f[u][0]+=max(f[v][0],f[v][1]);
            f[u][1]+=f[v][0];
        }
    }
}
int main() 
{
    memset(head,-1,sizeof(head));
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        int x;
        scanf("%lld%d",&a[i],&x);
        add(x,i);add(i,x);
    }
    for(int i=1;i<=n;i++)
        if(!vis[i])
        {
            flag=false;
            dfs(i,0);
            ll mx=0;
            dp(rl,0,rr);
            mx=max(f[rl][0],f[rl][1]);
            dp(rr,0,rl);
            mx=max(mx,max(f[rr][0],f[rr][1]));
            ans+=mx;
        }
    printf("%lld\n",ans);
}

猜你喜欢

转载自blog.csdn.net/qq_34531807/article/details/81150068