【BZOJ5329】【SDOI2018】战略游戏

【题目链接】

【思路要点】

  • 建立原图的圆方树。
  • 对于每个询问,建立其询问点集的虚树,分别考虑虚树的每一个点和每一条边上的圆点是否能够作为答案。
  • 简单树形DP即可。
  • 时间复杂度\(O(T(N+\sum S)LogN)\)。

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 200005;
const int MAXLOG = 20;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> 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 <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
struct edge {int len, dest; };
int oldn, n, m, q, top, Stack[MAXN];
int timer, dfn[MAXN], low[MAXN];
int ans, k, pos[MAXN], size[MAXN];
bool selected[MAXN];
int depth[MAXN], dist[MAXN], father[MAXN][MAXLOG];
vector <int> a[MAXN], b[MAXN];
vector <edge> c[MAXN];
void dfs(int pos, int fa) {
	dfn[pos] = ++timer;
	father[pos][0] = fa;
	depth[pos] = depth[fa] + 1;
	dist[pos] = dist[fa] + (pos <= oldn);
	for (int i = 1; i < MAXLOG; i++)
		father[pos][i] = father[father[pos][i - 1]][i - 1];
	for (unsigned i = 0; i < b[pos].size(); i++)
		if (b[pos][i] != fa) dfs(b[pos][i], pos);
}
void work(int pos) {
	bool tans = false;
	for (unsigned i = 0; i < c[pos].size(); i++) {
			work(c[pos][i].dest);
			size[pos] += size[c[pos][i].dest];
			if (size[c[pos][i].dest] != 0 && size[c[pos][i].dest] != k) ans += c[pos][i].len;
			tans |= size[c[pos][i].dest] != 0 && size[c[pos][i].dest] != k;
		}
	ans += tans && !selected[pos] && pos <= oldn;
}
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 = Stack[top--];
				b[++n].push_back(tmp);
				b[tmp].push_back(n);
				while (tmp != a[pos][i]) {
					tmp = Stack[top--];
					b[n].push_back(tmp);
					b[tmp].push_back(n);
				}
				b[n].push_back(pos);
				b[pos].push_back(n);
			}
		} else chkmin(low[pos], dfn[a[pos][i]]);
}
int lca(int x, int y) {
	if (depth[x] < depth[y]) swap(x, y);
	for (int i = MAXLOG - 1; i >= 0; i--)
		if (depth[father[x][i]] >= depth[y]) x = father[x][i];
	if (x == y) return x;
	for (int i = MAXLOG - 1; i >= 0; i--)
		if (father[x][i] != father[y][i]) {
			x = father[x][i];
			y = father[y][i];
		}
	return father[x][0];
}
bool cmp(int x, int y) {
	return dfn[x] < dfn[y];
}
int main() {
	int T; read(T);
	while (T--) {
		read(n), read(m), oldn = n;
		for (int i = 1; i <= n * 2; i++)
			a[i].clear(), b[i].clear();
		for (int i = 1; i <= m; i++) {
			int x, y; read(x), read(y);
			a[x].push_back(y);
			a[y].push_back(x);
		}
		memset(dfn, 0, sizeof(dfn));
		memset(low, 0, sizeof(low));
		timer = top = 0;
		tarjan(1);
		timer = 0;
		memset(dfn, 0, sizeof(dfn));
		dfs(1, 0);
		read(q);
		while (q--) {
			static int used[MAXN]; int tot = 0;
			read(k);
			for (int i = 1; i <= k; i++) {
				read(pos[i]);
				size[pos[i]] = 1;
				selected[pos[i]] = true;
			}
			sort(pos + 1, pos + k + 1, cmp);
			Stack[top = 1] = 1; used[tot = 1] = 1;
			for (int i = 1; i <= k; i++) {
				if (pos[i] == Stack[top]) continue;
				int Lca = lca(pos[i], Stack[top]);
				if (Lca == Stack[top]) {
					used[++tot] = pos[i]; Stack[++top] = pos[i];
					continue;
				}
				while (top >= 2 && dfn[Lca] < dfn[Stack[top - 1]]) {
					int x = Stack[top], y = Stack[--top];
					c[y].push_back((edge) {dist[x] - dist[y] - (x <= oldn), x});
				}
				int x = Stack[top--], y = Lca;
				c[y].push_back((edge) {dist[x] - dist[y] - (x <= oldn), x});
				if (Lca != Stack[top]) {
					Stack[++top] = Lca;
					used[++tot] = Lca;
				}
				Stack[++top] = pos[i];
				used[++tot] = pos[i];
			}
			while (top >= 2) {
				int x = Stack[top], y = Stack[--top];
				c[y].push_back((edge) {dist[x] - dist[y] - (x <= oldn), x});
			}
			ans = 0; work(1);
			for (int i = 1; i <= tot; i++) {
				size[used[i]] = 0;
				selected[used[i]] = false;
				c[used[i]].clear();
			}
			writeln(ans);
		}
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_39972971/article/details/80522883