P2607 [ZJOI2008]骑士 题解

Description

Luogu传送门

Solution

基环树上 \(dp\) 板子。

(但是为什么全机房除了我其他人都做过啊啊啊,果然还是我太菜了 \(QwQ\)

这道题给出的骑士之间的厌恶关系会形成基环树(森林),那么我们要在上面跑 \(dp\)

以下按一棵基环树来讨论。

我们先找到,然后选择上面的一个点为根,跑树形 \(dp\),然后再换成与它相邻的一个点为根跑一遍树形 \(dp\),取个最大值就是答案了(树形 \(dp\) 最下面再说)。

我们再来考虑基环树森林,其实是一样的。

主函数里枚举一遍所有的点,如果还没遍历过,就重复一次上述操作。

那么树形 \(dp\) 怎么跑?类似于 没有上司的舞会。

我们设 \(f_{x,0/1}\) 表示以 \(x\) 为根(\(x\) 选 或 不选)的子树中最大战斗力。

转移方程:

\[f_{x, 0} += max(f_{y, 0}, f_{y, 1}) \]

\[f_{x, 1} += f_{y, 0} \]

方程还是比较简单的,上代码。

Code

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#define ll long long

using namespace std;

inline int read(){
    int x = 0;
    char ch = getchar();
    while(!isdigit(ch)) ch = getchar();
    while(isdigit(ch)) x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
    return x;
}

const int inf = 1e9;
const int N = 1e6 + 10;
int n, rt;
ll ans;
int val[N], fa[N];
struct node{
    int v, nxt;
}edge[N << 1];
int head[N], tot;
bool vis[N];
ll f[N][2];

inline void add(int x, int y){
    edge[++tot] = (node){y, head[x]};
    head[x] = tot;
}

inline void dp(int x){
    vis[x] = 1;
    f[x][0] = 0, f[x][1] = val[x];
    for(int i = head[x]; i; i = edge[i].nxt){
        int y = edge[i].v;
        if(y == rt){
            f[y][1] = -inf;
            continue;
        }
        dp(y);
        f[x][0] += max(f[y][0], f[y][1]);
        f[x][1] += f[y][0];
    }
}

inline void dfs(int x){
    vis[x] = 1;
    rt = x;
    while(!vis[fa[rt]]) rt = fa[rt], vis[rt] = 1;
    dp(rt);
    ll res = max(f[rt][0], f[rt][1]);
    vis[rt] = 1, rt = fa[rt];
    dp(rt);
    ans += max(res, max(f[rt][0], f[rt][1]));
}

int main(){
    n = read();
    for(int i = 1; i <= n; ++i){
        val[i] = read(), fa[i] = read();
        add(fa[i], i);
    }
    for(int i = 1; i <= n; ++i)
        if(!vis[i]) dfs(i);
    printf("%lld\n", ans);
    return 0;
}

\[\_EOF\_ \]

posted @ 2021-11-10 20:02  xixike  阅读(38)  评论(0编辑  收藏  举报