P2607

· · 题解

Description

为了讨伐邪恶国度,现准备有 n 个骑士,其中第 i 个骑士讨厌骑士 f_i~(f_i\ne i),他是绝对不会与自己厌恶的人一同出征的。同时每名骑士有一个战斗力的估值 v_i,求最大化的所有出征骑士的战斗力总和。

Solution

if_i 建边,那么每个点就仅有一条出边,如果从一个点 i 不断沿着 f_i 走,总会陷入一个环中,因此不难想到它会是一棵内向基环树,整幅图就是一张内向基环森林。类似于:

内向基环树可以形象地理解为一个环上长了若干棵树,当然也存在某个没长树的环,那就把分开来考虑(显然两种情况的限制是不一样的):

时间复杂度 \mathcal O(n)

Code

在处理环的时候代码里的 g[i][1/0] 表示起始节点强制不选、dp[i][1/0] 表示起始节点强制选。

#include <iostream>
#define ll long long
using namespace std;
const int N = 1000006;
int n, f[N], out[N], q[N], back;
bool vis[N];
ll dp[N][2], g[N][2], ans;
int read(){
    int x = 0;
    char a = getchar();
    while(a < '0' || '9' < a) a = getchar();
    while('0' <= a && a <= '9') x = (x << 1) + (x << 3) + (a ^ 48), a = getchar();
    return x;
}
void write(ll x){
    if(x > 9) write(x / 10);
    putchar(x % 10 | 48);
}
int main(){
    n = read();
    for(int i = 1; i <= n; ++ i) dp[i][1] = read(), ++ out[f[i] = read()];
    for(int i = 1; i <= n; ++ i) if(!out[i]) q[++ back] = i;
    for(int front = 1; front <= back; ++ front){
        vis[q[front]] = 1;
        dp[f[q[front]]][0] += max(dp[q[front]][0], dp[q[front]][1]);
        dp[f[q[front]]][1] += dp[q[front]][0];
        if(!-- out[f[q[front]]]) q[++ back] = f[q[front]];
    }
    for(int i = 1, x; i <= n; ++ i)
        if(!vis[i]){
            g[i][0] = dp[i][0];
            for(x = i; f[x] != i; x = f[x]){
                vis[f[x]] = 1;
                g[f[x]][0] = dp[f[x]][0] + max(g[x][0], g[x][1]);
                g[f[x]][1] = dp[f[x]][1] + g[x][0];
                dp[f[x]][0] += max(dp[x][1], dp[x][0]);
                dp[f[x]][1] += dp[x][0];
            }
            ans += max(max(g[x][0], g[x][1]), dp[x][0]);
        }
    write(ans);
    return 0;
}

Weaken

P7752 [COCI2013-2014#2] PALETA

Similar

P1453 城市环路