P2607 [ZJOI2008]骑士 题解
Description
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\_
\]