【洛谷P4606】战略游戏

题目

题目链接:https://www.luogu.com.cn/problem/P4606
省选临近,放飞自我的小 (Q) 无心刷题,于是怂恿小 (C) 和他一起颓废,玩起了一款战略游戏。
这款战略游戏的地图由 (n) 个城市以及 (m) 条连接这些城市的双向道路构成,并且从任意一个城市出发总能沿着道路走到任意其他城市。
现在小 (C) 已经占领了其中至少两个城市,小 (Q) 可以摧毁一个小 (C) 没占领的城市,同时摧毁所有连接这个城市的道路。只要在摧毁这个城市之后能够找到某两个小 (C) 占领的城市 (u)(v),使得从 (u) 出发沿着道路无论如何都不能走到 (v),那么小 (Q) 就能赢下这一局游戏。
(Q) 和小 (C) 一共进行了 (q) 局游戏,每一局游戏会给出小 (C) 占领的城市集合 (S),你需要帮小 (Q) 数出有多少个城市在他摧毁之后能够让他赢下这一局游戏。
(1 ≤ T ≤ 10)(2 ≤ n ≤ 10^5)(n-1≤m≤2×10^5)(1 ≤ q ≤ 10^5),对于每组测试数据,有 (∑|S| ≤ 2 × 10^5)

思路

等价于询问任意选择两个集合内的点,其路径上必经点的并集大小。
Tarjan 把点双缩一下,建出圆方树,然后每一次询问将点按照圆方树上点的 dfn 排序。
枚举 dfn 序相邻两个点,他们之间的贡献为路径上圆点的数量。所以我们把圆点的权值设为 (1),方点为 (0),然后求出 LCA 就可以知道路径上权值之和了。注意需要减去 (u,v) 两个点的贡献 (2)
但是这样计算容易算重且不好容斥,所以我们把每一个点的权值放到他和他父节点之间的道路上,这样每一条路会被计算两次。最终答案除以 (2) 即可。但是我们这样会漏掉 dfn 序最小和最大的点 LCA 的贡献。所以再加回来即可。
时间复杂度 (O(sum nlog n))

代码

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

const int N=400010,LG=20;
int Q1,Q,n,m,t,ans,a[N];

struct edge
{
	int next,to;
};

struct Tree
{
	int tot,head[N],dfn[N],cnt[N],dep[N],f[N][LG+1];
	edge e[N];
	
	void add(int from,int to)
	{
		e[++tot]=(edge){head[from],to};
		head[from]=tot;
	}
	
	void dfs(int x,int fa)
	{
		dfn[x]=++tot; dep[x]=dep[fa]+1;
		cnt[x]=cnt[fa]+(x<=n);
		f[x][0]=fa;
		for (int i=1;i<=LG;i++)
			f[x][i]=f[f[x][i-1]][i-1];
		for (int i=head[x];~i;i=e[i].next)
		{
			int v=e[i].to;
			if (v!=fa) dfs(v,x);
		}
	}
	
	int lca(int x,int y)
	{
		if (dep[x]<dep[y]) swap(x,y);
		for (int i=LG;i>=0;i--)
			if (dep[f[x][i]]>=dep[y]) x=f[x][i];
		if (x==y) return x;
		for (int i=LG;i>=0;i--)
			if (f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
		return f[x][0];
	}
}T;

bool cmp(int x,int y)
{
	return T.dfn[x]<T.dfn[y];
}

struct Graph
{
	int tot,cnt,head[N],dfn[N],low[N];
	edge e[N];
	stack<int> st;
	
	void add(int from,int to)
	{
		e[++tot]=(edge){head[from],to};
		head[from]=tot;
	}
	
	void tarjan(int x)
	{
		dfn[x]=low[x]=++tot;
		st.push(x);
		for (int i=head[x];~i;i=e[i].next)
		{
			int v=e[i].to;
			if (!dfn[v])
			{
				tarjan(v);
				low[x]=min(low[x],low[v]);
				if (low[v]>=dfn[x])
				{
					int y; cnt++;
					do {
						y=st.top(); st.pop();
						T.add(cnt,y); T.add(y,cnt);
					}while (y!=v);
					T.add(x,cnt); T.add(cnt,x);
				}
			}
			else low[x]=min(low[x],dfn[v]);
		}
	}
}G;

void prework()
{
	while (G.st.size()) G.st.pop();
	memset(G.head,-1,sizeof(G.head));
	memset(G.dfn,0,sizeof(G.dfn));
	memset(T.head,-1,sizeof(T.head));
	G.tot=T.tot=0;
}

int main()
{
	scanf("%d",&Q1);
	while (Q1--)
	{
		prework();
		scanf("%d%d",&n,&m);
		for (int i=1,x,y;i<=m;i++)
		{
			scanf("%d%d",&x,&y);
			G.add(x,y); G.add(y,x);
		}
		G.tot=0; G.cnt=n;
		G.tarjan(1);
		T.tot=0;
		T.dfs(1,0);
		scanf("%d",&Q);
		while (Q--)
		{
			scanf("%d",&t);
			for (int i=1;i<=t;i++)
				scanf("%d",&a[i]);
			sort(a+1,a+1+t,cmp);
			a[++t]=a[1]; ans=0;
			for (int i=2;i<=t;i++)
			{
				int p=T.lca(a[i],a[i-1]);
				ans+=T.cnt[a[i]]+T.cnt[a[i-1]]-2*T.cnt[p]-2;
			}
			printf("%d
",ans/2+(T.lca(a[1],a[t-1])<=n));
		}
	}
	return 0;
}
原文地址:https://www.cnblogs.com/stoorz/p/14278985.html