思路:
这是基环树的入门题,因为
n
n
个骑士每个骑士有一个讨厌的骑士, 那么讨厌的骑士之间连接一条边,很显然选出的骑士之间不能有边存在,如果这是一棵树的话可以树形:
dp[pre][0]=∑max(dp[son][0],dp[son][1])
d
p
[
p
r
e
]
[
0
]
=
∑
m
a
x
(
d
p
[
s
o
n
]
[
0
]
,
d
p
[
s
o
n
]
[
1
]
)
dp[pre][1]=∑dp[son][0]
d
p
[
p
r
e
]
[
1
]
=
∑
d
p
[
s
o
n
]
[
0
]
基环树
dp
d
p
其实就是选中环上的一条边,设变为
e
e
,连接的两点为,那么先暂时删除这条边使得这幅图成为一棵树,然后进行两次树形
dp
d
p
,一次是以
u
u
为根,一次是以为根,最终的结果是取 第一次
dp
d
p
后的
dp[u][0]
d
p
[
u
]
[
0
]
和 第二次
dp
d
p
后的
dp[v][0]
d
p
[
v
]
[
0
]
的最大值即为答案,为什么呢,既然
u,v
u
,
v
是相邻的点,那么要么
u
u
不选,要么不选, 要么
u,v
u
,
v
都不选,那这样的话两次
dp
d
p
之后这三种情况都考虑进去了,然后取最大值即为答案, 一些注意的是可能存在
u,v
u
,
v
互相讨厌的情况, 去下重就好了,最后可能图不连通,需要处理整个基环森林。
#include<bits/stdc++.h>
typedef long long ll;
const int maxn = 1e6 + 10;
using namespace std;
int n, m, T, kase = 1;
int hate[maxn], vis[maxn];
ll val[maxn], dp[maxn][2];
vector<int> G[maxn];
typedef pair<int, int> pa;
void solve_dp(int x, int fa, pa now) {
dp[x][0] = 0; dp[x][1] = val[x]; vis[x] = 1;
for(int i = 0; i < G[x].size(); i++) {
int v = G[x][i]; if(v == fa) continue;
if(v == now.first && x == now.second) continue;
if(v == now.second && x == now.first) continue; ///去除边
solve_dp(v, x, now);
dp[x][0] += max(dp[v][0], dp[v][1]); ///不选x这个节点
dp[x][1] += dp[v][0]; ///选择x这个节点
}
}
pa find_circle(int x, int fa) {
vis[x] = 1; pa ans(-1, -1);
for(int i = 0; i < G[x].size(); i++) {
int v = G[x][i];
if(v == fa) continue;
if(vis[v]) return pa(v, x);
ans = find_circle(v, x);
if(~ans.first) return ans;
}
return pa(-1, -1);
}
ll solve(int x) {
pa edge = find_circle(x, 0);
if(edge.first == -1) {
solve_dp(x, 0, edge);
return max(dp[x][0], dp[x][1]);
}
solve_dp(edge.first, 0, edge);
ll ans = dp[edge.first][0];
solve_dp(edge.second, 0, edge);
ans = max(ans, dp[edge.second][0]);
return ans;
}
int main() {
while(scanf("%d", &n) != EOF) {
for(int i = 0; i < maxn; i++) {
vis[i] = 0; G[i].clear();
}
for(int i = 1; i <= n; i++) {
scanf("%lld %d", &val[i], &hate[i]);
G[i].push_back(hate[i]);
G[hate[i]].push_back(i);
}
for(int i = 1; i <= n; i++) {
sort(G[i].begin(), G[i].end());
G[i].erase(unique(G[i].begin(), G[i].end()), G[i].end());
}
ll ans = 0;
for(int i = 1; i <= n; i++) if(!vis[i]) ans += solve(i);
printf("%lld\n", ans);
}
return 0;
}