BZOJ1040

BZOJ1040 [ZJOI2008]骑士

Description

Z 国有N个骑士,每个骑士有一个战斗力和一个讨厌的骑士(不会讨厌自己)。

骑士按 1 N编号。

现在要选出一些骑士组成一个骑士团,要求骑士团内的每个骑士所讨厌的骑士都不在骑士团内。求:满足条件的骑士团的最大战斗力和是多少?

Input

第一行:一个数 N ,表示骑士数。

接下来N行:每行两个整数,分别表示当前骑士的战斗力和他讨厌的骑士的编号

Output

一个整数:表示最大战斗力和。

Solution

A 讨厌B,则 AB 不能同时入选,等价于 B 讨厌A,所以应该是无向边。

N 个点N条边,很明显就是环套树。同一条边上的点不能同时入选,求最大和,显然用树形DP。

深搜一遍,发现环就把环上的一个点拆掉,假设被拆掉的边的两个点分别为 u v,就在 u v上分别做树形DP,再比较两点不取时的最大和,比较得出答案。

Code

#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<string.h>
#include<stack>
#include<queue>

using namespace std;
const int maxn=1000100;
int n,heat[maxn],zdl[maxn]; 
long long ans,sum;
struct EDGE
{
    int u,v,num;
    EDGE *next;
}edge[maxn*2],*ind[maxn];
int el=-1;
void input(int u,int v)
{
    edge[++el].u=u;
    edge[el].v=v;
    edge[el].num=el;
    edge[el].next=ind[u];
    ind[u]=&edge[el];
}

int visit[maxn],cuta,cutb,cute;
void dfs(int u,int fa)
{
    visit[u]=1;
    for(EDGE *k=ind[u];k!=NULL;k=k->next)
    {
        int v=k->v;
        if(v==fa) continue;
        if(visit[v])
        {
            cuta=u;
            cutb=v;
            cute=k->num;
            continue;
        }
        dfs(v,u);
    }
}

long long int f[maxn],g[maxn];

long long dp(int u,int fa)
{
    f[u]=zdl[u];
    g[u]=0;
    for(EDGE *k=ind[u];k!=NULL;k=k->next)
    {
        int v=k->v;
        if(v==fa||k->num==cute||(k->num^1)==cute) continue;
        f[u]+=dp(v,u);
        g[u]+=max(f[v],g[v]);
    }
    return g[u];
}

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d",&zdl[i],&heat[i]);
        input(heat[i],i);
        input(i,heat[i]);
    }
    for(int i=1;i<=n;i++)
    {
        if(visit[i]==0)
        {
            dfs(i,0);
            sum=max(dp(cutb,0),dp(cuta,0));
            ans+=sum;

        }
    }
    printf("%lld",ans);
    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值