【题解 P4606】战略游戏

[SDOI2018] 战略游戏

题目描述

省选临近,放飞自我的小 Q 无心刷题,于是怂恿小 C 和他一起颓废,玩起了一款战略游戏。

这款战略游戏的地图由 \(n\) 个城市以及 \(m\) 条连接这些城市的双向道路构成,并且从任意一个城市出发总能沿着道路走到任意其他城市。

现在小 C 已经占领了其中至少两个城市,小 Q 可以摧毁一个小 C 没占领的城市,同时摧毁所有连接这个城市的道路。只要在摧毁这个城市之后能够找到某两个小 C 占领的城市 \(u\)\(v\),使得从 \(u\) 出发沿着道路无论如何都不能走到 \(v\),那么小 Q 就能赢下这一局游戏。

小 Q 和小 C 一共进行了 \(q\) 局游戏,每一局游戏会给出小 C 占领的城市集合 \(S\),你需要帮小 Q 数出有多少个城市在他摧毁之后能够让他赢下这一局游戏。

输入格式

第一行包含一个正整数 \(T\),表示测试数据的组数。

对于每组测试数据:

第一行是两个整数 \(n\)\(m\) ,表示地图的城市数和道路数。

接下来 \(m\) 行,每行包含两个整数 \(u\)\(v (1 \le u < v \le n)\),表示第 \(u\) 个城市和第 \(v\) 个城市之间有一条道路,同一对城市之间可能有多条道路连接。

\(m + 1\) 是一个整数 \(q\),表示游戏的局数,

接下来 \(q\) 行,每行先给出一个整数 \(|S| (2 \le |S| \le n)\),表示小 C 占领的城市数量,然后给出 \(|S|\) 个整数 \((1 \le S_1 < S_2 < \cdots < S_{|S|} ≤ n)\),表示小 C 占领的城市。

输出格式

对于每一局游戏,输出一行,包含一个整数,表示这一局游戏中有多少个城市在小 Q 摧毁之后能够让他赢下这一局游戏。

样例 #1

样例输入 #1

2
7 6
1 2
1 3
2 4
2 5
3 6
3 7
3
2 1 2
3 2 3 4
4 4 5 6 7
6 6
1 2
1 3
2 3
1 4
2 5
3 6
4
3 1 2 3
3 1 2 6
3 1 5 6
3 4 5 6

样例输出 #1

0
1
3
0
1
2
3

提示

  • \(1 \le T \le 10\)
  • \(2 \le n \le 10^5\)\(n - 1 \le m \le 2\times 10 ^ 5\)
  • \(1 \le q \le 10^5\)
  • 对于每组测试数据,有 \(\sum|S| \le 2 \times 10^5\)

Subtasks

  • 子任务 1 (30 分):对于每组测试数据,满足 \(\sum|S| \le 20\)
  • 子任务 2 (45 分):对于每一次询问,满足 \(|S| = 2\)
  • 子任务 3 (25 分):没有任何附加的限制。

原题地址

解法

看见删点后有两点不能到达,考虑割点。
看见 \(\sum|S| \le 2 \times 10^5\) ,考虑虚树。
然后就是建一个圆方树,查询建虚树处理就好了。
但怎么查两点之间的割点数呢?
首先,若一个点为圆点(即不是点双造出来的点),他与父节点的连边的权值为1,否则为0。
设查询 \(m\) 个点 \(a_1,a_2,a_3,......,a_m\),将 \(a_1,a_2\)\(a_2,a_3\) \(......\) \(a_m,a_1\) 的路径上的权值加起来,除以二,即为这个联通块内割点的数量。
还有是点转边,所以若联通块的根( \(LCA(a_1,a_m)\) )为圆点,要加上1。
由于占领的点不能删,还有减去点的个数。
输出答案即可。

代码

#include<bits/stdc++.h>
using namespace std;
long long n,m,dfn[200005],low[100005],num,co,f[200005][19],deep[200005],f1[200005][19],val,m1,b[200005],r,d1[200005];
vector<long long> a[100005],d[200005],t[200005];
stack<long long> l;
bool v[200005];
void tarjan(long long x,long long y)
{
	dfn[x]=low[x]=++num;
	l.push(x);
	for(int i=0;i<a[x].size();i++)
	{
		if(a[x][i]==y)continue;
		if(!dfn[a[x][i]])
		{
			tarjan(a[x][i],x);
			low[x]=min(low[x],low[a[x][i]]);
			if(low[a[x][i]]>=dfn[x])
			{
				co++;
				while(l.top()!=a[x][i])
				{
					d[co].push_back(l.top());
					l.pop();
				}
				l.pop();
				d[co].push_back(a[x][i]);
				d[co].push_back(x); 
			}
		}
		else low[x]=min(low[x],dfn[a[x][i]]);
	}
	return;
}
void dfs1(long long x,long long y)
{
	f[x][0]=y;
	deep[x]=deep[y]+1;
	dfn[x]=++num;
	for(int i=0;i<t[x].size();i++)
	{
		if(t[x][i]==y)continue;
		f1[t[x][i]][0]=v[t[x][i]];
		dfs1(t[x][i],x);
	}
	return;
}
long long LCA(long long x,long long y)
{
	if(deep[x]<deep[y])swap(x,y);
	val=0;
	for(int i=18;i>=0;i--)
	{
		if(deep[f[x][i]]>=deep[y])val+=f1[x][i],x=f[x][i];
	}
	if(x==y)return x;
	for(int i=18;i>=0;i--)
	{
		if(f[x][i]!=f[y][i])val+=f1[x][i]+f1[y][i],x=f[x][i],y=f[y][i];
	}
	val+=f1[x][0]+f1[y][0];
	x=f[x][0];
	y=f[y][0];
	return x;
}
bool cmp1(long long x,long long y)
{
	return dfn[x]<dfn[y];
}
void poi()
{
	memset(dfn,0,sizeof(dfn));
	memset(low,0,sizeof(low));
	memset(f,0,sizeof(f));
	memset(deep,0,sizeof(deep));
	memset(f1,0,sizeof(f1));
	memset(v,false,sizeof(v));
	num=co=0;
	long long x,y;
	scanf("%lld%lld",&n,&m);
	for(int i=1;i<=m;i++)
	{
		scanf("%lld%lld",&x,&y);
		a[x].push_back(y);
		a[y].push_back(x);
	}
	for(int i=1;i<=n;i++)
	{
		if(!dfn[i])tarjan(i,0);
		while(l.size()!=0)l.pop();
	}
	for(int i=1;i<=co;i++)
	{
		for(int j=0;j<d[i].size();j++)
		{
			t[n+i].push_back(d[i][j]);
			t[d[i][j]].push_back(n+i);
		}
	}
	for(int i=1;i<=n;i++)
	{
		v[i]=true;
	}
	num=0;
	dfs1(1,0);
	for(int i=1;i<=18;i++)
	{
		for(int j=1;j<=n+co;j++)f[j][i]=f[f[j][i-1]][i-1],f1[j][i]=f1[j][i-1]+f1[f[j][i-1]][i-1];
	}
	long long q,s;
	scanf("%lld",&m);
	for(int i=1;i<=m;i++)
	{
		s=0;
		scanf("%lld",&m1);
		for(int j=1;j<=m1;j++)
		{
			scanf("%lld",&b[j]);
		}
		sort(b+1,b+m1+1,cmp1);
		for(int j=1;j<m1;j++)
		{
			LCA(b[j],b[j+1]);
			s+=val;
		}
		LCA(b[1],b[m1]);
		s+=val;
		s/=2;
		s+=v[LCA(b[1],b[m1])];
		s-=m1;
		printf("%lld\n",s);
	} 
	for(int i=1;i<=n;i++)a[i].clear(); 
	for(int i=1;i<=co;i++)d[i].clear();
	for(int i=1;i<=co+n;i++)t[i].clear();
	return;
}
int main()
{
	long long qwe;
	scanf("%lld",&qwe);
	for(int i=1;i<=qwe;i++)poi(); 








  return 0;
}
posted @ 2023-08-27 22:15  dijah  阅读(104)  评论(0编辑  收藏  举报