【BZOJ1023】【SHOI2008】仙人掌图

【题目链接】

  • 点击打开链接

【思路要点】

  • 建立圆方树,并进行树形DP,求出每个圆点到其子树内最远的圆点的距离\(dp_{i,0}\),以及在不同的子树内距离最远的圆点的距离\(dp_{i,1}\)。
  • 考虑枚举直径上离根最近的点:
  • 若该点为圆点,那么该圆点对答案的贡献显然为\(dp_{i,0}+dp_{i,1}\)。
  • 若该点为方点,那么问题便转化为了“在一个\(N\)个点的环上有一系列点,每个点有权值\(val_i\),现在要选出两个点\(i\),\(j\),使得\(val_i+val_j+dist(i,j)\)最大”。倍长环,转化为序列问题。不妨令\(i
  • 时间复杂度\(O(N)\)。

【代码】

#include
using namespace std;
const int MAXN = 100005;
template  void chkmax(T &x, T y) {x = max(x, y); }
template  void chkmin(T &x, T y) {x = min(x, y); } 
template  void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
template  void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template  void writeln(T x) {
	write(x);
	puts("");
}
vector  a[MAXN], b[MAXN];
int n, m, oldn, ans;
int top, Stack[MAXN], len[MAXN];
int timer, dfn[MAXN], low[MAXN];
int dp[MAXN][2];
void dfs(int pos, int fa) {
	if (pos <= oldn) {
		for (unsigned i = 0; i < b[pos].size(); i++)
			if (b[pos][i] != fa) dfs(b[pos][i], pos);
	} else {
		int tmp = 0;
		for (unsigned i = 0; i < b[pos].size(); i++)
			if (b[pos][i] != fa) {
				int dest = b[pos][i];
				dfs(dest, pos);
				int tnp = b[pos].size() - 1 - i;
				chkmax(tmp, dp[dest][0] + min(tnp, len[pos] - tnp));
			}
		if (tmp > dp[fa][0]) {
			dp[fa][1] = dp[fa][0];
			dp[fa][0] = tmp;
		} else chkmax(dp[fa][1], tmp);
	}
}
void tarjan(int pos) {
	Stack[++top] = pos;
	dfn[pos] = low[pos] = ++timer;
	for (unsigned i = 0; i < a[pos].size(); i++)
		if (dfn[a[pos][i]] == 0) {
			tarjan(a[pos][i]);
			chkmin(low[pos], low[a[pos][i]]);
			if (low[a[pos][i]] >= dfn[pos]) {
				int tmp = 0, last = 0; n++;
				while (tmp != a[pos][i]) {
					last = tmp;
					tmp = Stack[top--];
					b[n].push_back(tmp);
					b[tmp].push_back(n);
					len[n]++;
				}
				b[n].push_back(pos);
				b[pos].push_back(n);
				len[n]++;
			}
		} else chkmin(low[pos], dfn[a[pos][i]]);
}
int main() {
	read(n), read(m), oldn = n;
	for (int i = 1; i <= m; i++) {
		int k; read(k);
		int pos; read(pos);
		for (int j = 2; j <= k; j++) {
			int x; read(x);
			a[x].push_back(pos);
			a[pos].push_back(x);
			pos = x;
		}
	}
	tarjan(1);
	dfs(1, 0);
	ans = 0;
	for (int i = 1; i <= oldn; i++)
		chkmax(ans, dp[i][0] + dp[i][1]);
	for (int t = oldn + 1; t <= n; t++) {
		static int val[MAXN]; int tot = 0, limit = len[t] / 2;
		for (unsigned i = 0; i < b[t].size(); i++)
			if (i == b[t].size() - 1) val[++tot] = 0;
			else val[++tot] = dp[b[t][i]][0];
		for (unsigned i = 0; i < b[t].size(); i++)
			if (i == b[t].size() - 1) val[++tot] = 0;
			else val[++tot] = dp[b[t][i]][0];
		static int q[MAXN]; int l = 0, r = -1;
		for (int i = 1; i <= limit; i++) {
			while (l <= r && val[i] + i >= val[q[r]] + q[r]) r--;
			q[++r] = i;
		}
		for (int i = 1; i <= len[t]; i++) {
			if (q[l] == i) l++;
			int j = i + limit;
			while (l <= r && val[j] + j >= val[q[r]] + q[r]) r--;
			q[++r] = j;
			chkmax(ans, val[i] + val[q[l]] + q[l] - i);
		}
	}
	writeln(ans);
	return 0;
}

你可能感兴趣的:(【OJ】BZOJ,【类型】做题记录,【数据结构】圆方树,【数据结构】队列与单调队列,【算法】动态规划)