[bzoj5329][圆方树][虚树]战略游戏

5 篇文章 0 订阅
2 篇文章 0 订阅

Description

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

Input

第一行包含一个正整数T,表示测试数据的组数, 对于每组测试数据, 第一行是两个整数n和m,表示地图的城市数和道路数,
接下来m行,每行包含两个整数u和v~(1<=u<v<=n) 表示第u个城市和第v个城市之间有一条道路,同一对城市之间可能有多条道路连接,
第m+1是一个整数q,表示游戏的局数, 接下来q行,每行先给出一个整数|S|(2<=|S|<=n)
表示小C占领的城市数量,然后给出|S|个整数s1,s2,…s|S|,(1<=s1<s2<s|S|<=n),表示小C占领的城市。 1<=
T<= 10, 2<= n<= 10^5 且 n-1<= m<= 210^5, 1<= q<= 10^5,
对于每组测试数据,有Sigma|S|<= 2
10^5

Output

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

Sample Input

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

Sample Output

0

1

3

0

1

2

3

题解

这题嘛…
把圆方树建出来
答案就是这些点构成的虚树中的圆点总数
大力排序一下…
如果根也是圆点的话答案要加1

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#include<ctime>
#define LL long long
#define mp(x,y) make_pair(x,y)
using namespace std;
inline int read()
{
	int f=1,x=0;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
inline void write(int x)
{
	if(x<0)putchar('-'),x=-x;
	if(x>9)write(x/10);
	putchar(x%10+'0');
}
inline void print(int x){write(x);printf(" ");}
struct node{int x,y,next;}a[410000],b[810000];int len1,last1[210000],len2,last2[410000];
void ins_a(int x,int y){len1++;a[len1].x=x;a[len1].y=y;a[len1].next=last1[x];last1[x]=len1;}
void ins_b(int x,int y){len2++;b[len2].x=x;b[len2].y=y;b[len2].next=last2[x];last2[x]=len2;}
int low[210000],dfn[210000],sta[210000],id,cnt,tp;
int r[210000];
void link()
{
	cnt++;
	for(int i=1;i<=r[0];i++)ins_b(cnt,r[i]),ins_b(r[i],cnt);
}
void tarjan(int x)
{
	dfn[x]=low[x]=++id;sta[++tp]=x;
	for(int k=last1[x];k;k=a[k].next)
	{
		int y=a[k].y;
		if(dfn[y]==-1)
		{
			tarjan(y);low[x]=min(low[x],low[y]);
			if(low[y]>dfn[x])ins_b(x,y),ins_b(y,x),tp--;
			else if(low[y]==dfn[x])
			{
				r[r[0]=1]=x;
				int i;
				do
				{
					i=sta[tp--];
					r[++r[0]]=i;
				}while(i!=y);
				link();
			}
		}
		else low[x]=min(low[x],dfn[y]);
	}
}
int in[410000],dpos;
int sum[410000],fa[410000][25],dep[410000],bin[25];
int n,m,S,po[210000];
void pre_tree_node(int x)
{
	in[x]=++dpos;
	for(int i=1;bin[i]<=dep[x];i++)fa[x][i]=fa[fa[x][i-1]][i-1];
	for(int k=last2[x];k;k=b[k].next)
	{
		int y=b[k].y;
		if(y!=fa[x][0])
		{
			fa[y][0]=x;dep[y]=dep[x]+1;sum[y]=sum[x]+(y<=n);
			pre_tree_node(y);
		}
	}
}
int lca(int x,int y)
{
	if(dep[x]<dep[y])swap(x,y);
	for(int i=20;i>=0;i--)if(bin[i]<=dep[x]&&dep[fa[x][i]]>=dep[y])x=fa[x][i];
	if(x==y)return x;
	for(int i=20;i>=0;i--)if(bin[i]<=dep[x]&&fa[x][i]!=fa[y][i])x=fa[x][i],y=fa[y][i];
	return fa[x][0];
}
bool cmp(int n1,int n2){return in[n1]<in[n2];}
int sol()
{
	sort(po+1,po+1+S,cmp);
	int ret=-2*S;
	for(int i=2;i<=S;i++)
	{
		int LA=lca(po[i-1],po[i]);
		ret+=sum[po[i-1]]+sum[po[i]]-2*sum[LA];
	}
	int LA=lca(po[S],po[1]);
	ret+=sum[po[S]]+sum[po[1]]-2*sum[LA];
	ret/=2;
	if(sum[LA]!=sum[fa[LA][0]])ret++;
	return ret;
}
int main()
{
//	freopen("01.in","r",stdin);
//	freopen("a.out","w",stdout);
	bin[0]=1;for(int i=1;i<=20;i++)bin[i]=bin[i-1]<<1;
	int T=read();
	while(T--)
	{
		len1=0;memset(last1,0,sizeof(last1));
		len2=0;memset(last2,0,sizeof(last2));
		n=read();m=read();
		for(int i=1;i<=m;i++)
		{
			int x=read(),y=read();
			ins_a(x,y);ins_a(y,x);
		}
		cnt=n;id=tp=0;
		memset(low,0,sizeof(low));
		memset(dfn,-1,sizeof(dfn));
		tarjan(1);
//		for(int i=1;i<=len2;i+=2)printf("%d %d\n",b[i].x,b[i].y);
		memset(sum,0,sizeof(sum));
		dep[1]=0;sum[1]=1;dpos=0;
		pre_tree_node(1);
		int Q=read();
		while(Q--)
		{
			S=read();
			for(int i=1;i<=S;i++)po[i]=read();
			printf("%d\n",sol());
		}
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值