(File IO): input:game.in output:game.out
Time Limits: 10000 ms Memory Limits: 524288 KB
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) ( 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) ( 1 ≤ s 1 < s 2 < ⋯ < s | S | ≤ n ) ,表示小 C 占领的城市。
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
Output
0
1
3
0
1
2
3
Data Constraint
对于 30%的分数, ∑∣S∣≤20。
对于另外 45%的分数,对于每一次询问,满足 ∣S∣=2 。
对于 100% 的分数, 1≤T≤10,2≤n≤10^5,n−1≤m≤2×10^5,1≤q≤10^5,且对于每组测试数据,有 ∑∣S∣≤2×10^5。
Analysis
这题一看就知道和割点或点双有关
那么就要将点双缩起来,但是缩没那么好缩
这里用圆方树
什么是圆方树,对于一个点双,用一个方形代表,其他点用圆形表示,点双内的点与方形相连,那么构成一个圆方相间的树。
这样只用对查询建一颗虚树,询问圆点个数
具体实现如下
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 400100
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define fo2(i,x,g) for(int i=g.fir[x];i;i=g.nex[i])
using namespace std;
int n,m,f[20][N],dfn[N],low[N],cnt,q,top,sta[N],dep[N],s,d[N];
bool vis[N];
bool cmp(int a,int b){return dfn[a]<dfn[b];}
struct graph{
int fir[N],nex[N+N],to[N+N],top;
void clear(){
memset(fir,0,sizeof fir);
top=0;
}
void link(int x,int y){
to[++top]=y;nex[top]=fir[x];fir[x]=top;
to[++top]=x;nex[top]=fir[y];fir[y]=top;
}
}g1,g2;
void tarjan(int u,int f){
vis[u]=1;
dfn[u]=low[u]=++top;
sta[top]=u;
fo2(i,u,g1)if(g1.to[i]!=f){
int v=g1.to[i];
if(!vis[v]){
tarjan(v,u);
low[u]=min(low[u],low[v]);
if(dfn[u]<=low[v]){
g2.link(++cnt,u);
for(;sta[top+1]!=v;--top)g2.link(cnt,sta[top]);
}
}else low[u]=min(low[u],dfn[v]);
}
}
void dfs(int x,int fa){
f[0][x]=fa;
dfn[x]=++top;dep[x]=dep[fa]+1;
fo2(i,x,g2)if(g2.to[i]!=fa)dfs(g2.to[i],x);
}
int lca(int x,int y){
if(dep[x]>dep[y])return lca(y,x);
fd(i,19,0)if(dep[f[i][y]]>=dep[x])y=f[i][y];
if(x==y)return x;
fd(i,19,0)if(f[i][y]!=f[i][x])x=f[i][x],y=f[i][y];
return f[0][x];
}
int main(){
freopen("game.in","r",stdin);
freopen("game.out","w",stdout);
int t;scanf("%d",&t);
while(t--){
g1.clear();g2.clear();
memset(vis,0,sizeof vis);
scanf("%d %d",&n,&m);
cnt=n;top=0;
fo(i,1,m){
int x,y;scanf("%d %d",&x,&y);
g1.link(x,y);
}
tarjan(1,0);
top=0;dfs(1,0);
fo(j,1,19)fo(i,1,cnt)f[j][i]=f[j-1][f[j-1][i]];
scanf("%d",&q);
fo(i,1,q){
int ans=0;
scanf("%d",&s);
fo(j,1,s)scanf("%d",&d[j]);
sort(d+1,d+s+1,cmp);top=0;
fo(j,1,s)if(!top)sta[top=1]=d[j];else{
int u=lca(sta[top],d[j]);
ans+=(dep[sta[top]]+1>>1)-(dep[u]+1>>1);
while(top && dep[sta[top]]>=dep[u])--top;
sta[++top]=u;sta[++top]=d[j];
}ans+=(dep[sta[top]]+1>>1)-(dep[sta[1]]+1>>1);
printf("%d\n",ans+(dep[sta[1]]&1)-s);
}
}
fclose(stdin);fclose(stdout);
return 0;
}