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