ZJOI2008 骑士
自己写的时候建的是 \(dls\) 的反图 , 想的是基环树不是要保证每个点的出度为 \(1\) , 就选择每个点向仇恨点连接一条有向边.
这种情况下如果记录每一个点出度指向哪 , 那么在找环的时候不一定能找到 , 因为图上带环的话要根据入度点找环 (画图理解)
如果记录入度指向哪 , 这是这样建图你不能保证每一个点都有入度
所以按 \(dls\) 的方式建边 , 保证每一个点均有入读点(父亲) , 且满足基环树性质 (反过来入度为 1)
#include <bits/stdc++.h>
using ll = long long;
const ll INF = 1E18;
int main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int n;
std::cin >> n;
std::vector<std::vector<int>> g(n);
std::vector<int> val(n) , p(n);
for (int i = 0 ; i < n ; ++i) {
std::cin >> val[i] >> p[i];
p[i]--;
g[p[i]].push_back(i);
}
ll ans = 0;
std::vector<int> vis(n , 0) , oncyc(n , 0);
std::vector<std::array<ll,2>> dp1(n , {0 , 0}) , dp2(n);
for (int i = 0 ; i < n ; ++i) {
if (vis[i]) continue;
//利用基环树出度是 1 的性质
int u = i;
while (!vis[u]) {
vis[u] = 1 ; u = p[u];
}
int v = u;
std::vector<int> cyc;
while (true) {
oncyc[v] = 1;
cyc.push_back(v);
v = p[v];
if (u == v) break;
}
std::function<void(int)> dfs = [&] (int u) {
dp1[u][1] = val[u] , vis[u] = true;
for (auto v : g[u]) {
if (oncyc[v]) continue;
dfs(v);
dp1[u][0] += std::max(dp1[v][0] , dp1[v][1]);
dp1[u][1] += dp1[v][0];
}
};
//树形 dp
int m = cyc.size();
for (auto v : cyc)
dfs(v);
//环形 dp , 拆环为链 , 枚举环上第一个点的状态
ll res = 0;
for (int t = 0 ; t < 2 ; ++t) {
for (int s = 0 ; s < 2 ; ++s) {
if (s != t) dp2[0][s] = -INF;
else dp2[0][s] = dp1[cyc[0]][s];
}
for (int i = 1 ; i < m ; ++i) {
int u = cyc[i];
dp2[i][0] = dp1[u][0] + std::max(dp2[i - 1][0] , dp2[i - 1][1]);
dp2[i][1] = dp1[u][1] + dp2[i - 1][0];
}
if (t == 0) res = std::max({res , dp2[m - 1][0] , dp2[m - 1][1]});
else res = std::max(res , dp2[m - 1][0]);
}
ans += res;
}
std::cout << ans << '\n';
return 0;
}
标签:std,dp2,dp1,int,res,代码,cyc,基环树 From: https://www.cnblogs.com/xqy2003/p/17602880.html