[BZOJ1040][[ZJOI2008]骑士][基环外向树+树形DP]
题目大意:
给定一个 N<=1000000 个点的基环外向森林,求带权的最大独立集。
思路:
首先题目中看上去给出的是有向边但实际上是无向边,因为
v
不会和
这道题一开始我以为只是一棵普通的树,那这样直接树形
dp
就好了,用
f[u][1],f[u][0]
表示
u
点选中或者不选的最大值,不妨设显然就是:
再读题发现自己傻逼了,题目总共给出了
N
个点
再打完发现自己又傻逼了,显然所有骑士并不一定会连在一起,肯定是只有一棵基环外向树,另外有或没有很多棵普通的树。但其实还是一样搞,在最外层枚举一下每个点有没有 vis 过就好了,然后把每棵树的贡献加入 ans
断边可以直接把边设为
false
,但边是双向边而前向星的存法只能一条边表示一个方向,但(两级反转)加边的时候是两个方向的边是同时加入的,所有一条边的编号是
id
,另一条边的编号就是
id⊕1
,处理起来很方便。
代码:
#include <bits/stdc++.h>
typedef long long ll;
const int Maxn = 1000010;
using namespace std;
inline ll Max(const ll &a, const ll &b) {
return a > b ? a : b;
}
namespace IO {
inline char get(void) {
static char buf[1000000], *p1 = buf, *p2 = buf;
if (p1 == p2) {
p2 = (p1 = buf) + fread(buf, 1, 1000000, stdin);
if (p1 == p2) return EOF;
}
return *p1++;
}
inline void read(ll &x) {
x = 0; static char c;
for (; !(c >= '0' && c <= '9'); c = get());
for (; c >= '0' && c <= '9'; x = x * 10 + c - '0', c = get());
}
};
ll head[Maxn], sub;
struct Edge {
ll to, nxt;
bool ok;
Edge(void) {}
Edge(const int &to, const int &nxt) : to(to), nxt(nxt) { ok = 1; }
} edge[Maxn << 1];
inline void add(int a, int b) {
edge[sub] = Edge(b, head[a]), head[a] = sub++;
}
ll n, val[Maxn], U, V;
ll f[2][2][Maxn], ans, fa[Maxn];
bool vis[Maxn], vis1[Maxn];
inline void Dp(ll u, ll w) {
f[w][1][u] = val[u];
vis[u] = 1;
for (int i = head[u], v; ~i; i = edge[i].nxt) {
v = edge[i].to;
if (!edge[i].ok || f[w][1][v]) continue;
Dp(v, w);
f[w][0][u] += Max(f[w][1][v], f[w][0][v]);
f[w][1][u] += f[w][0][v];
}
vis[u] = 0;
}
queue<int> qu;
inline ll work(ll u) {
vis1[u] = 1;
U = V = -1;
qu.push(u);
bool ok = false;
while (!qu.empty()) {
int k = qu.front(); qu.pop();
for (int i = head[k], v; ~i; i = edge[i].nxt) {
v = edge[i].to;
if (!vis1[v] || fa[k] != v) {
if (vis1[v]) {
edge[i].ok = edge[i ^ 1].ok = 0;
U = k, V = v;
ok = true;
break;
}
vis1[v] = 1;
fa[v] = k;
qu.push(v);
}
}
if (ok) {
while (!qu.empty()) qu.pop();
}
}
//cout << U << ' ' << V << endl;
if (!ok) return Dp(u, 0), Max(f[0][0][u], f[0][1][u]);
else return Dp(U, 0), Dp(V, 1), Max(f[0][0][U], f[1][0][V]);
}
int main(void) {
//freopen("in.txt", "r", stdin);
IO::read(n);
memset(head, -1, sizeof head);
for (ll i = 1, fa; i <= n; i ++) {
IO::read(val[i]); IO::read(fa);
add(fa, i), add(i, fa);
}
for (int i = 1; i <= n; i++)
if (!f[0][1][i]) {
ans += work(i);
}
cout << ans << endl;
return 0;
}
完。
By g1n0st