1. 程式人生 > >[bzoj5329][圓方樹][虛樹]戰略遊戲

[bzoj5329][圓方樹][虛樹]戰略遊戲

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;
}