[SDOI2018]战略游戏

题目

圆方树其实并没有那么难

圆方树的构建比较简单,就是一个tarjan把点双跑出来,对于每一个点双我们多建一个方点,把原图中的点称为圆点,将点双内所有圆点向方点连边,之后我们就得到了原图的圆方树

关于圆方树的性质,zyb大爷在他的题解里写了很多,这里就不再抄一遍了

至于这道题,就是把圆点拿出来建棵虚树,割虚树上的圆点就会使得点集不连通了

那么如何求虚树中的圆点个数呢,我们可以将圆点的点权放到其连向父亲的那条边上去,这样统计虚树的边权和就好了;特殊的,当虚树的最高点为圆点时,它连向父亲的边不在虚树里,所以需要额外计算

代码,就是tarjan板子和虚树板子,写起来爽就是了

代码

#include<bits/stdc++.h>
#define re register
#define min(a,b) ((a)<(b)?(a):(b))
const int maxn=2e5+7;
inline int read() {
	char c=getchar();int x=0;while(c<'0'||c>'9') c=getchar();
	while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
}
struct E{int v,nxt;}e[maxn<<1];
int n,m,num,pre[maxn],head[maxn],sum[maxn],cnt,pt[maxn],Top[maxn];
int st[maxn],son[maxn],dfn[maxn],ans,dep[maxn],top,fa[maxn],rn;
inline int cmp(int A,int B) {return dfn[A]<dfn[B];}
inline void add(int x,int y) {
	e[++num].v=y;e[num].nxt=head[x];head[x]=num;
	e[++num].v=x;e[num].nxt=head[y];head[y]=num;
}
void dfs1(int x) {
	sum[x]=1;son[x]=0;Top[x]=0;
	for(re int i=head[x];i;i=e[i].nxt) {
		if(dep[e[i].v]) continue;
		dep[e[i].v]=dep[x]+1;fa[e[i].v]=x;
		pre[e[i].v]=pre[x]+(e[i].v<=rn);
		dfs1(e[i].v);sum[x]+=sum[e[i].v];
		if(sum[e[i].v]>sum[son[x]]) son[x]=e[i].v;
	}
}
void dfs2(int x,int topf) {
	dfn[x]=++cnt;Top[x]=topf;
	if(!son[x]) return;dfs2(son[x],topf);
	for(re int i=head[x];i;i=e[i].nxt) if(!Top[e[i].v]) dfs2(e[i].v,e[i].v);
}
inline int LCA(int x,int y) {
	while(Top[x]!=Top[y]) {
		if(dep[Top[x]]<dep[Top[y]]) y=fa[Top[y]];
		else x=fa[Top[x]];
	}
	return dep[x]<dep[y]?x:y;
}
inline void ins(int x) {
	if(top<1) {st[++top]=x;return;}
	int lca=LCA(st[top],x);
	if(lca==st[top]) {st[++top]=x;return;}
	while(top>1&&dfn[st[top-1]]>=dfn[lca])
		ans+=pre[st[top]]-pre[st[top-1]],--top;
	if(lca!=st[top]) ans+=pre[st[top]]-pre[lca],st[top]=lca;
	st[++top]=x;
}
namespace Build {
	struct E{int v,nxt;}e[maxn<<1];
	int cnt,top,num;
	int head[maxn>>1],dfn[maxn>>1],low[maxn>>1],st[maxn>>1];
	inline void add_(int x,int y) {
		e[++num].v=y;e[num].nxt=head[x];head[x]=num;
		e[++num].v=x;e[num].nxt=head[y];head[y]=num;
	}
	void tarjan(int x,int fa) {
		dfn[x]=low[x]=++cnt;
		for(re int i=head[x];i;i=e[i].nxt)
			if(!dfn[e[i].v]) {
				st[++top]=e[i].v;tarjan(e[i].v,x);low[x]=min(low[x],low[e[i].v]);
				if(low[e[i].v]>=dfn[x]) {
					++n;do{add(st[top],n);}while(st[top--]!=e[i].v);
					add(x,n);
				}
			}else if(x!=fa) low[x]=min(low[x],dfn[e[i].v]);
	}
	inline void solve() {
		cnt=top=num=0;memset(head,0,sizeof(head));memset(st,0,sizeof(st));
		memset(dfn,0,sizeof(dfn));memset(low,0,sizeof(low));
		for(re int i=1;i<=m;i++) add_(read(),read());
		tarjan(1,0);
	}
}
int main() {
	for(re int T=read();T;--T) {
		rn=n=read(),m=read();num=0;
		memset(head,0,sizeof(head));memset(dep,0,sizeof(dep));
		Build::solve();cnt=0;dep[1]=1,dfs1(1);dfs2(1,1);
		for(re int sz,Q=read();Q;--Q) {
			sz=read();top=0;ans=0;
			for(re int i=1;i<=sz;i++) pt[i]=read();
			std::sort(pt+1,pt+sz+1,cmp);
			for(re int i=1;i<=sz;i++) ins(pt[i]);
			while(top>1) ans+=pre[st[top]]-pre[st[top-1]],--top;
			printf("%d\n",ans+(st[1]<=rn)-sz);
		}
	}
	return 0;
}
posted @ 2019-12-11 16:06  asuldb  阅读(140)  评论(1编辑  收藏  举报