首页 > 代码库 > BZOJ1040 [ZJOI2008]骑士

BZOJ1040 [ZJOI2008]骑士

题意:基环树最大独立集


思路:

像这种题就是朴素的树形dp非常容易的,我们用一些技巧转化为变体树。

直接套用仙人掌的动态规划做法:(基环树事实上也属于一种仙人掌)

首先利用tarjan算法,如果遇到自己与儿子之间的边为割边则按照树边处理。

Tarjan后看一下与自己相连的边,如果某个相邻点不是自己的儿子,并且入栈序比自己大,那么说明自己是环上的的最高点,此时我们对环上特别的进行dp,在这之后将环上所有点的决策值转移到环上的最高点上,那么可以认为这个环连带着环下的子树都被缩成这一个点了。于是向上返回即可。

最终我们只需要输出一开始进行tarjan那个点的最优值即可。


Code:

#include <cstdio>
#include <cstring>
#include <cctype>
#include <iostream>
#include <algorithm>
using namespace std;

typedef long long LL;

#define N 1000010

int head[N], next[N << 1], end[N << 1];
void addedge(int a, int b) {
	static int q = 1;
	end[q] = b;
	next[q] = head[a];
	head[a] = q++;
}

int w[N];

LL dp[N][2];

int dfn[N], low[N], pa[N], tclock;

int sav[N], size;
LL work[N][2];
void Dp(int top, int bottom) {
	int tmp = bottom;
	size = 0;
	for(; tmp != top; tmp = pa[tmp])
		sav[++size] = tmp;
	sav[++size] = top;
	
	register int i;
	work[1][0] = dp[sav[1]][0], work[1][1] = dp[sav[1]][1];
	for(i = 2; i <= size; ++i) {
		work[i][0] = max(work[i - 1][0], work[i - 1][1]) + dp[sav[i]][0];
		work[i][1] = work[i - 1][0] + dp[sav[i]][1];
	}
	dp[top][0] = work[size][0];
	
	work[1][0] = dp[sav[1]][0], work[1][1] = -1LL << 60;
	for(i = 2; i <= size; ++i) {
		work[i][0] = max(work[i - 1][0], work[i - 1][1]) + dp[sav[i]][0];
		work[i][1] = work[i - 1][0] + dp[sav[i]][1];
	}
	dp[top][1] = work[size][1];
}

void dfs(int x) {
	dfn[x] = low[x] = ++tclock;
	dp[x][1] = w[x];
	for(int j = head[x]; j; j = next[j]) {
		if (!dfn[end[j]]) {
			pa[end[j]] = x;
			dfs(end[j]);
			if (low[end[j]] > dfn[x]) {
				dp[x][1] += dp[end[j]][0];
				dp[x][0] += dp[end[j]][0] > dp[end[j]][1] ? dp[end[j]][0] : dp[end[j]][1];
			}
			low[x] = min(low[x], low[end[j]]);
		}
		else if (end[j] != pa[x])
			low[x] = min(low[x], dfn[end[j]]);
	}
	for(int j = head[x]; j; j = next[j])
		if (pa[end[j]] != x && dfn[x] < dfn[end[j]])
			Dp(x, end[j]);
}
int main() {
	#ifndef ONLINE_JUDGE
	freopen("tt.in", "r", stdin);
	#endif
	
	int n;
	scanf("%d", &n);
	
	register int i, j;
	int x;
	for(i = 1; i <= n; ++i) {
		scanf("%d%d", &w[i], &x);
		addedge(i, x);
		addedge(x, i);
	}
	
	LL tot = 0;
	for(i = 1; i <= n; ++i) {
		if (!dfn[i]) {
			dfs(i);
			tot += dp[i][0] > dp[i][1] ? dp[i][0] : dp[i][1];
		}
	}
	
	printf("%lld", tot);
	
	return 0;
}

BZOJ1040 [ZJOI2008]骑士