BZOJ 1040 [ZJOI2008]骑士

由于一个骑士只会痛恨另一个骑士,所以痛恨的关系等于骑士数量。可知这是一张由n个点,n条边组成的环套树森林。

环套树是什么?环套树是一种有且只有一个环的图(并没有找到环套树的标准定义-_-)。大概脑补一下,就是一个环,上面的一些节点套着一棵树。。。

记f[i][1或0]表示节点i取或不取,它以及他的子树的贡献答案。

环套树问题一般都涉及拆环上的边。此题中,可以随意删除环上的一条边,记被删除的边两端节点为u,v,那这一棵环套树的答案就是max(f[u][0],f[v][0]),因为u和v至少有一个不会被取。

把每棵环套树都做过去就好了,记得开long long。

#include<cstdio>
#include<algorithm>
#define MAXN 1000010
using namespace std;
int n, a[MAXN], edge_cnt=1, u, v, cut, q[MAXN], last[MAXN], vis[MAXN], vis_cnt=1;
long long f[MAXN][2];
struct edge{int next,to;}e[MAXN<<1];
void add(int a, int b)
{
    e[++edge_cnt]=(edge){last[a],b};
    last[a]=edge_cnt;
}
void bfs(int beg)
{
    q[0]=beg;
    vis[beg]=++vis_cnt;
    for(int head=0, tail=1; head<tail; head++)
    {
        int x=q[head];
        for(int i = last[x]; i; i=e[i].next)
        {
            int y=e[i].to;
            if(vis[y] && vis[y]+1==vis[x])
                continue;
            else if(vis[y])
            {
                u=x;
                v=y;
                cut=i;
            }
            else
            {
                q[tail++]=y;
                vis[y]=vis[x]+1;
            }
        }
    }
}
void dp(int x, int fa)
{
    f[x][1]=a[x];
    f[x][0]=0;
    for(int i = last[x]; i; i=e[i].next)
    {
        if(i==cut || (i^1)==cut || e[i].to==fa)continue;
        int y=e[i].to;
        dp(y,x);
        f[x][1]+=f[y][0];
        f[x][0]+=max(f[y][0],f[y][1]);
    }
}
int main()
{
    long long ans=0, pre;
    scanf("%d",&n);
    for(int i = 1, x; i <= n; i++)
    {
        scanf("%d%d",&a[i],&x);
        add(i,x);
        add(x,i);
    }
    for(int i = 1; i <= n; i++)
        if(!vis[i])
        {
            bfs(i);
            dp(u,-1);
            pre=f[u][0];
            dp(v,-1);
            ans+=max(pre,f[v][0]);
        }
    printf("%lld\n",ans);
    return 0;
} 
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值