有 n n n 个骑士,每个骑士有讨厌的人(不是自己),选出一个骑士团,没有讨厌的人和自己在一个骑士团。问骑士团的最大的战斗力。
类似没有上司的舞会。
这题有很多坑:不一定是根节点处在环内;二元环的处理;有多个连通块,这是一个基环树森林,需要对答案相加;答案暴 i n t int int 。
上司的舞会
相同;#pragma region
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
#define rep(i, a, n) for (int i = a; i <= n; ++i)
#define per(i, a, n) for (int i = n; i >= a; --i)
#pragma endregion
const int maxn = 1e6 + 5;
int n;
int a[maxn], hat[maxn], d[maxn];
int head[maxn], ver[maxn << 1], C[maxn << 1], nxt[maxn << 1];
int tot = 0;
void add(int u, int v, int w) {
ver[++tot] = v;
C[tot] = w;
nxt[tot] = head[u];
head[u] = tot;
}
ll dp[maxn][2];
bool vis[maxn];
void init(int u, int f, int id) {
dp[u][0] = 0, dp[u][1] = a[u];
for (int i = head[u]; i; i = nxt[i]) {
int v = ver[i];
if (v == f) continue;
if (C[i] == id) continue;
init(v, u, id);
}
}
void dfs(int u, int f, int id) {
vis[u] = 1;
for (int i = head[u]; i; i = nxt[i]) {
int v = ver[i];
if (v == f) continue;
if (C[i] == id) continue;
dfs(v, u, id);
dp[u][1] += dp[v][0];
dp[u][0] += max(dp[v][0], dp[v][1]);
}
}
void topo() {
queue<int> q;
rep(i, 1, n) if (d[i] == 1) q.push(i);
while (q.size()) {
int now = q.front();
q.pop();
for (int i = head[now]; i; i = nxt[i]) {
int nex = ver[i];
if (!d[nex]) continue;
d[nex]--, d[now]--;
if (d[nex] == 1) q.push(nex);
break;
}
}
}
int main() {
scanf("%d", &n);
rep(i, 1, n) {
scanf("%d%d", &a[i], &hat[i]);
add(hat[i], i, i);
add(i, hat[i], i);
d[i]++, d[hat[i]]++;
}
topo();
int rt = 1, hrt;
ll ans = 0;
while (1) {
for (; rt <= n; ++rt)
if (d[rt] && !vis[rt]) break;
if (rt > n) break;
int id = 0;
for (int i = head[rt]; i; i = nxt[i]) {
int v = ver[i];
if (d[v]) {
hrt = v, id = C[i];
break;
}
}
init(rt, 0, id);
dp[rt][1] = -1e18; //不选rt
dfs(rt, 0, id);
ll tmp = dp[rt][0];
init(rt, 0, id);
dp[hrt][1] = -1e18; //选rt
dfs(rt, 0, id);
ans += max(tmp, dp[rt][1]);
}
printf("%lld\n", ans);
}