bzoj 1040: [ZJOI2008]骑士(树形DP)

题目链接:https://cn.vjudge.net/contest/246776#problem/E

题目大意:每个骑士都有自己厌恶的人和自己的战斗力,求一个军队中每一个人所厌恶的人都不在,军团战斗力为所有骑士战斗力的和,问军团的战斗力最大为多少。

思路:https://blog.csdn.net/qq_36782366/article/details/81674459

和没有上司的舞会类似。只是多了一个环。

因此我们先要找到树上的环,任何删除任意环上的一条边,加上删的这条边的两个端点为i,j。

接下来就要以i和j为根结点分别跑一边,因为i和j的状态有3种,i选j不选,i不选j选,i和j都不选。所以检测一下哪种情况的战斗力最大。

注意输入的是基环森林,不一定都在一棵树上。

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
const int MAXN = 1e6+100;
int n,a[MAXN],cnt,l,r,edgenum;
int head[MAXN];
bool vis[MAXN];
ll dp[MAXN][2];

struct Edge
{
    int to,nex;
}edge[MAXN*2];

void addedge(int u,int v)
{
    edge[cnt] = {v,head[u]};
    head[u] = cnt++;
    edge[cnt] = {u,head[v]};
    head[v] = cnt++;
}

void loop(int u,int fa)
{
    for (int i=head[u];~i;i=edge[i].nex)
    {
        int v = edge[i].to;
        if (v == fa) continue;
        if (!vis[v])
        {
            vis[v] = true;
            loop(v,u);
        }
        else
        {
            edgenum = i;
            l = u; r = v;
        }
    }
}

void dfs(int u,int fa)
{
    dp[u][0] = 0;
    dp[u][1] = a[u];
    for (int i=head[u];~i;i=edge[i].nex)
    {
        int v = edge[i].to;
        if ( v == fa || ((i^1) == edgenum) || i == edgenum ) continue;
        dfs(v,u);
        dp[u][1] += dp[v][0];
        dp[u][0] += max( dp[v][1], dp[v][0] );
    }
}



int main()
{
    memset(head,-1,sizeof head);
    scanf("%d",&n);
    cnt=0;
    for (int i=1;i<=n;i++)
    {
        int to;
        scanf("%d%d",&a[i],&to);
        addedge( i,to );
    }
    ll ans = 0;
    for (int i=1;i<=n;i++)
    {
        if (vis[i]) continue;
        loop(i,0);
        dfs(l,0);
        ll tmp = dp[l][0];
        dfs(r,0);
        ans += max(tmp, dp[r][0]);
    }
    printf("%lld\n",ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_36782366/article/details/81674705