P4606 [SDOI2018] 战略游戏 对自己的警告--zhengjun

发布时间 2023-07-12 19:02:46作者: A_zjzj

tarjan 多测的时候 dfn 数组要清空!!!

树剖多测的时候 son 数组要清空!!!

点双 tarjan 时可用 vector 建边,边双时用 vector 需要无重边

本题直接建圆方树,然后答案就是关键点构成的虚树上非关键原点个数。

代码

#include<bits/stdc++.h>
using namespace std;
using ll=long long;
const int N=2e5+10,V=N*2;
int T,n,m,k,q;
struct edges{
	int to,nex;
}edge[N*2];
int kk=1,head[N];
void addedge(int u,int v){
	edge[++kk]={v,head[u]},head[u]=kk;
	edge[++kk]={u,head[v]},head[v]=kk;
}
vector<int>to[V];
int cnt,dft,dfn[N],low[N],stk[N];
void add(int u,int v){
	to[u].push_back(v),to[v].push_back(u);
}
void tarjan(int u,int las=0){
	low[u]=dfn[u]=++dft,stk[++cnt]=u;
	for(int i=head[u],v;v=edge[i].to,i;i=edge[i].nex)if(i^las^1){
		if(!dfn[v]){
			tarjan(v,i),low[u]=min(low[u],low[v]);
			if(low[v]>=dfn[u]){
				add(++k,u);
				do add(k,stk[cnt]);while(stk[cnt--]^v);
			}
		}
		else low[u]=min(low[u],dfn[v]);
	}
}
int tot,fa[V],son[V],siz[V],dep[V],top[V],sum[V],bg[V];
void dfs1(int u){
	sum[u]=sum[fa[u]]+(u<=n);
	siz[u]=1,dep[u]=dep[fa[u]]+1;
	son[u]=0;
	for(int v:to[u])if(v^fa[u]){
		fa[v]=u,dfs1(v);
		siz[u]+=siz[v];
		if(siz[v]>siz[son[u]])son[u]=v;
	}
}
void dfs2(int u,int t){
	top[u]=t,bg[u]=++tot;
	if(son[u])dfs2(son[u],t);
	for(int v:to[u])if(v^fa[u]&&v^son[u])dfs2(v,v);
}
int LCA(int u,int v){
	for(;top[u]^top[v];u=fa[top[u]]){
		if(dep[top[u]]<dep[top[v]])swap(u,v);
	}
	return dep[u]<dep[v]?u:v;
}
int t,a[N];
void get(){
	scanf("%d%d",&n,&m);
	for(int u,v;m--;){
		scanf("%d%d",&u,&v);
		addedge(u,v); 
	}
	k=n,cnt=dft=0,tarjan(1);
	tot=0,dfs1(1),dfs2(1,1);
	scanf("%d",&q);
	for(;q--;){
		scanf("%d",&t);
		for(int i=1;i<=t;i++)scanf("%d",&a[i]);
		sort(a+1,a+1+t,[](int x,int y){return bg[x]<bg[y];});
		stk[cnt=1]=a[1];
		int ans=0;
		auto add=[&](int t,int u){
			ans+=sum[u]-sum[t];
		};
		for(int i=2;i<=t;i++){
			int x=LCA(a[i],stk[cnt]);
			for(;cnt>1&&dep[stk[cnt-1]]>=dep[x];cnt--)add(stk[cnt-1],stk[cnt]);
			if(stk[cnt]^x)add(x,stk[cnt--]);
			if(stk[cnt]^x)stk[++cnt]=x;
			if(stk[cnt]^a[i])stk[++cnt]=a[i];
		}
		for(int i=1;i<cnt;i++)add(stk[i],stk[i+1]);
		ans+=stk[1]<=n;
		printf("%d\n",ans-t);
	}
}
void clear(){
	kk=1;
	for(int i=1;i<=n;i++)head[i]=dfn[i]=0;
	for(int i=1;i<=k;i++)to[i].clear();
}
int main(){
	freopen(".in","r",stdin);
	//freopen(".out","w",stdout);
	for(scanf("%d",&T);T--;clear())get();
	return 0;
}