传送门
在基环树上求解最大点独立集问题。
有点学术啊。那我先来解释一下吧。
基环树 顾名思义,就是基于一个环上的树,也就是一个树中有一个环。由于原题中把骑士看成结点,憎恨关系看成边之后就得到了一个有 n n 个结点, n n 条边的图,我们知道由 n n 个结点, n−1 n − 1 条边组成的连通图是一棵树。那么拥有 n n 个结点和 n n 条边的连通图就是一棵基环树。这样一来,原题的图就可以抽象成一个基环树森林。
一个图的点独立集是原图总点集的一个子集,且点独立集中任意两个顶点均不相邻。简单点来说就是从图中挑点,所取的点两两不相邻。
图的最大点独立集就是顶点数最多的点独立集。
但是本题是带点权的,这样一来就要稍微更改一下定义,我们定义图的最大带权点独立集就是总权重(集合中所有点的权重的和)最大的点独立集。
假如这是个森林的话就很简单,就是 没有上司的舞会 。
这里面详细介绍了怎样解决树上的最大点独立集问题,这里就不再赘述。
但是带权的话,状态转移方程就要修改一下:
我们定义 d(i,0) d ( i , 0 ) 表示在以 i i 为根的子树中,不取 i i 所能达到的最大权重。
相应的, d(i,1) d ( i , 1 ) 表示以 i i 为根的子树中,取 i i 所能达到的最大权重。
考虑到基环树删去环上的一条边就变成了一棵树(含有 n n 个结点及 n−1 n − 1 条边且保证连通)。
我们就可以设计出一个算法:找到这个基环树中的环,并找到其上任意一条边 (u,v) ( u , v ) (所有边的地位等同),然后把它“删掉”(在dp过程中不经过这条边),让它变成一棵树,接下来我们考虑添加这条边对结果的影响。
很显然,多了这条边, u u 和 v v 不可以同时选取,这样子就会有三种情况:
但是这样是很难处理的。我们可以考虑合并成两种情况:
这样就可以解决问题了。
我们在solve完 u u 结点之后,保存 p=d(u,0) p = d ( u , 0 ) ,再solve v v 结点,保存 q=d(v,0) q = d ( v , 0 ) 再取 max{p,q} max { p , q } 加入 Ans A n s 即可。这样一来就保证了 u u 和 v v 不会同时选中,也保证了解的最优性。
#include
#include
#include
#include
#define MAXN 1000010
#define max(x, y) ((x) > (y) ? (x) : (y))
int value[MAXN], a, b;
long long f[MAXN][2];
struct edgeType {
int to, next;
} edge[MAXN << 1];
int head[MAXN], vis[MAXN], n, s, x, y, cut;
void addEdge(int from, int to) {
static int cnt = 0;
edge[cnt] = (edgeType){to, head[from]};
head[from] = cnt++;
}
void findCycle(int u, int p) {
vis[u] = 1;
for (int i = head[u]; ~i; i = edge[i].next) {
if ((i ^ 1) == p) continue;
int v = edge[i].to;
if (vis[v]) {
x = u, y = v;
cut = i;
continue;
}
findCycle(v, i);
}
}
void solve(int u, int p) {
f[u][0] = 0;
f[u][1] = value[u];
for (int i = head[u]; ~i; i = edge[i].next) {
if ((i ^ 1) == p) continue;
if (i == cut || (i ^ 1) == cut)
continue;
int v = edge[i].to;
dfs(v, i);
f[u][1] += f[v][0];
f[u][0] += max(f[v][1], f[v][0]);
}
}
int main() {
memset(head, -1, sizeof head);
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%d%d", &a, &b);
addEdge(i, b);
addEdge(b, i);
value[i] = a;
}
long long ans = 0;
for (int i = 1; i <= n; i++) {
if (vis[i]) continue;
findCycle(i, -2);
solve(x, -1);
long long temp = f[x][0];
solve(y, -1);
temp = max(temp, f[y][0]);
ans += temp;
}
printf("%lld", ans);
return 0;
}