「SDOI2018」战略游戏-圆方树

Description

有一张无向图,每次给定一个集合 S S ,求有多少个点满足删除这个点后, S S 中至少有两个点不连通。

n 1 0 5 n \leq 10^5 m , S 2 × 1 0 5 m,\sum |S| \leq 2\times 10^5

Solution

考虑所求就是 S S 在圆方树上虚树的圆点数- S |S|

但实际并不需要把虚树求出来,考虑把一个圆点的权值放在它到父亲方点的边上,那么把 S S d f s dfs 序排序后,答案为

d i s ( S 1 , S 2 ) + d i s ( S 2 , S 3 ) + . . . + d i s ( S n 1 , S n ) + d i s ( S n , S 1 ) 2 S \frac{dis(S_1,S_2)+dis(S_2,S_3)+...+dis(S_{n-1},S_n)+dis(S_n,S_1)}{2}-|S|

这个可以画图感受一下。

注意当虚树上最浅点为圆点时,要加上它的权值。

#include <bits/stdc++.h>
using namespace std;

inline int gi()
{
	char c = getchar();
	while(c < '0' || c > '9') c = getchar();
	int sum = 0;
	while('0' <= c && c <= '9') sum = sum * 10 + c - 48, c = getchar();
	return sum;
}

const int maxn = 100005;

int n, m, s[maxn];

struct edge
{
	int to, next;
} e[maxn << 2];
int h[maxn << 1], tot, cnt;

inline void add(int u, int v)
{
	e[++tot] = (edge) {v, h[u]}; h[u] = tot;
	e[++tot] = (edge) {u, h[v]}; h[v] = tot;
}

namespace Tarjan
{

	struct edge
	{
		int to, next;
	} e[maxn << 2];
	int h[maxn], tot;
	
	inline void Add(int u, int v)
	{
		e[++tot] = (edge) {v, h[u]}; h[u] = tot;
		e[++tot] = (edge) {u, h[v]}; h[v] = tot;
	}

	int dfn[maxn], low[maxn], Time, stk[maxn], top;

	void dfs(int u)
	{
		dfn[u] = low[u] = ++Time;
		stk[++top] = u;
		for (int i = h[u], v; v = e[i].to, i; i = e[i].next)
			if (!dfn[v]) {
				dfs(v);
				low[u] = min(low[u], low[v]);
				if (dfn[u] == low[v]) {
					++cnt;
					while (stk[top + 1] != v)
						add(stk[top], cnt), --top;
					add(cnt, u);
				}
			} else low[u] = min(low[u], dfn[v]);
	}
	
}

int fa[maxn << 1], dep[maxn << 1], dis[maxn << 1], siz[maxn << 1], son[maxn << 1], top[maxn << 1], dfn[maxn << 1], low[maxn << 1], Time;
int vis[maxn << 1];

void dfs1(int u)
{
	vis[u] = 1;
	dep[u] = dep[fa[u]] + 1; dis[u] = dis[fa[u]] + (u <= n); siz[u] = 1; son[u] = 0;
	for (int i = h[u], v; v = e[i].to, i; i = e[i].next)
		if (v != fa[u]) {
			if (vis[v]) 
				puts("call me"), exit(0);
			fa[v] = u; dfs1(v); siz[u] += siz[v];
			if (siz[v] > siz[son[u]]) son[u] = v;
		}
}

void dfs2(int u)
{
	dfn[u] = ++Time;
	if (son[u]) top[son[u]] = top[u], dfs2(son[u]);
	for (int i = h[u], v; v = e[i].to, i; i = e[i].next)
		if (v != son[u] && v != fa[u]) top[v] = v, dfs2(v);
	low[u] = Time;
}

inline int lca(int u, int v)
{
	while (top[u] != top[v]) {
		if (dep[top[u]] > dep[top[v]]) u = fa[top[u]];
		else v = fa[top[v]];
	}
	return dep[u] < dep[v] ? u : v;
}

int main()
{
	int T = gi();
	
	while (T--) {
		n = gi(); m = gi();
		fill(h + 1, h + cnt + 1, 0); tot = 0;
		fill(Tarjan::h + 1, Tarjan::h + n + 1, 0); Tarjan::tot = 0;
		fill(Tarjan::dfn + 1, Tarjan::dfn + n + 1, 0);
		for (int i = 1; i <= m; ++i) Tarjan::Add(gi(), gi());

		cnt = n; Tarjan::Time = 0; 
		Tarjan::dfs(1); --Tarjan::top;

		memset(vis + 1, 0, sizeof(vis));
		dfs1(1);
		Time = 0; top[1] = 1; dfs2(1);
		
		int q = gi(), k;
		while (q--) {
			k = gi();
			for (int i = 1; i <= k; ++i) s[i] = gi();
			sort(s + 1, s + k + 1, [](const int &a, const int &b) {return dfn[a] < dfn[b];});
			int ans = -2 * k;
			for (int i = 1; i < k; ++i) ans += dis[s[i]] + dis[s[i + 1]] - (dis[lca(s[i], s[i + 1])] << 1);
			ans += dis[s[k]] + dis[s[1]] - (dis[lca(s[k], s[1])] << 1);
			if (lca(s[1], s[k]) <= n) ans += 2;
			printf("%d\n", ans >> 1);
		}
	}
	
	return 0;
}

猜你喜欢

转载自blog.csdn.net/DSL_HN_2002/article/details/89508314