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;
}

發佈了54 篇原創文章 · 獲贊 3 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章