BZOJ1040-[ZJOI2008]骑士


题解:
题意:有一个基环树森林,每个点有权值,取出互不相邻的任意个点,问最大权值和是多少。
先考虑是一棵树的情况:
dp[i][0/1] d p [ i ] [ 0 / 1 ] 表示以 i i 为根的子树中i (1) ( 1 ) 或不取 (0) ( 0 ) 的最大权值和
dp[u][0]=max(dp[v][0],dp[v][1]) d p [ u ] [ 0 ] = ∑ m a x ( d p [ v ] [ 0 ] , d p [ v ] [ 1 ] )
dp[u][1]=a[u]+dp[v][0] d p [ u ] [ 1 ] = a [ u ] + ∑ d p [ v ] [ 0 ]
那加一条边变成了环之后呢?
取出环上相邻的两个点 x x y,将连边断开,变成一棵树,而这两个点不能同时取。
分别以这两个点为根做树形 dp d p
ans=max(dp1[x][0],dp2[y][0]) a n s = m a x ( d p 1 [ x ] [ 0 ] , d p 2 [ y ] [ 0 ] )
Code 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);
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jack-Oran

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值