5329: [Sdoi2018]战略游戏
Time Limit: 30 Sec Memory Limit: 512 MBSubmit: 12 Solved: 9
[Submit][Status][Discuss]
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<= 2*10^5,
1<= q<= 10^5,
对于每组测试数据,有Sigma|S|<= 2*10^5
Output
对于每一局游戏,输出一行,包含一个整数,表示这一局游戏中有多少个城市在小Q摧毁之后能够让他赢下这一局游戏。
Sample Input
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
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
3
0
1
2
3
考场上想出来发现是个圆方树。。。但是以前没写过怎么办啊QWQ
不管了,硬钢吧23333
(然后最后就神奇的钢出来了)
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<stack>
#include<vector>
#include<map>
#include<tr1/unordered_map>
#define ll long long
#define pb push_back
#define mid (l+r>>1)
#define lc (o<<1)
#define rc ((o<<1)|1)
using namespace std;
using namespace std::tr1;
const int maxn=400005;
vector<int> g[maxn];
unordered_map<int,int> mmp[maxn];
int T,n,m,num,hd[maxn],to[maxn*2],ne[maxn*2];
int dfn[maxn],low[maxn],dc,cnt,rp[maxn],dy[maxn],dep[maxn];
int siz[maxn],son[maxn],cl[maxn],F[maxn],L,Q,now[maxn],k,ans;
int Lim[maxn*4],sum[maxn*4],le,ri,W,tag[maxn*4];
struct lines{ int U,V;};
stack<lines> s;
inline int read(){
int x=0; char ch=getchar();
for(;!isdigit(ch);ch=getchar());
for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';
return x;
}
void Wt(int x){ if(x<0) { Wt(-x); return;} if(x>=10) Wt(x/10); putchar(x%10+'0');}
inline void add(int x,int y){ to[++num]=y,ne[num]=hd[x],hd[x]=num;}
inline void ADD(int x,int y){ g[x].pb(y),g[y].pb(x);}
inline void init(){
for(int i=0;i<=cnt;i++) g[i].clear(),mmp[i].clear();
memset(hd,0,sizeof(hd)),num=0;
memset(dfn,0,sizeof(dfn)),dc=0;
memset(son,0,sizeof(son));
}
void dfs(int x,int fa){
low[x]=dfn[x]=++dc;
lines e;
for(int i=hd[x];i;i=ne[i]) if(to[i]!=fa){
e=(lines){x,to[i]};
if(!dfn[to[i]]){
s.push(e);
dfs(to[i],x),low[x]=min(low[x],low[to[i]]);
if(low[to[i]]>=dfn[x]){
cnt++;
for(;;){
e=s.top(),s.pop();
if(!mmp[e.U].count(cnt)) ADD(e.U,cnt),mmp[e.U][cnt]=1;
if(!mmp[e.V].count(cnt)) ADD(e.V,cnt),mmp[e.V][cnt]=1;
if(e.U==x&&e.V==to[i]) break;
}
}
}
else if(dfn[to[i]]<low[x]){
low[x]=dfn[to[i]];
s.push(e);
}
}
}
void dfs1(int x,int fa){
F[x]=fa,siz[x]=1;
for(int i=g[x].size()-1,T;i>=0;i--){
T=g[x][i];
if(T==fa) continue;
rp[T]=rp[x]+(T<=n),dep[T]=dep[x]+1;
dfs1(T,x),siz[x]+=siz[T];
if(!son[x]||siz[son[x]]<siz[T]) son[x]=T;
}
}
void dfs2(int x,int tp){
cl[x]=tp,dfn[x]=++dc,dy[dc]=x;
if(!son[x]) return;
dfs2(son[x],tp);
for(int i=g[x].size()-1,t;i>=0;i--){
T=g[x][i];
if(T==F[x]||T==son[x]) continue;
dfs2(T,T);
}
}
void build(int o,int l,int r){
sum[o]=tag[o]=0;
if(l==r){ Lim[o]=(dy[l]<=n); return;}
build(lc,l,mid),build(rc,mid+1,r);
Lim[o]=Lim[lc]+Lim[rc];
}
inline void work1(int o){ sum[o]=Lim[o],tag[o]=1;}
inline void work2(int o){ sum[o]=0,tag[o]=2;}
inline void pushdown(int o){
if(tag[o]==1){ work1(lc),work1(rc);}
else if(tag[o]){ work2(lc),work2(rc);}
tag[o]=0;
}
inline void maintain(int o){
sum[o]=sum[lc]+sum[rc];
}
void update1(int o,int l,int r){
if(l>=le&&r<=ri){ ans+=Lim[o]-sum[o],work1(o); return;}
pushdown(o);
if(le<=mid) update1(lc,l,mid);
if(ri>mid) update1(rc,mid+1,r);
maintain(o);
}
void update2(int o,int l,int r){
if(l>=le&&r<=ri){ work2(o); return;}
pushdown(o);
if(le<=mid) update2(lc,l,mid);
if(ri>mid) update2(rc,mid+1,r);
maintain(o);
}
inline int LCA(int x,int y){
while(cl[x]!=cl[y]){
if(dep[cl[x]]>dep[cl[y]]) x=F[cl[x]];
else y=F[cl[y]];
}
return dep[x]>dep[y]?y:x;
}
inline void paint(int x){
while(x){
le=dfn[cl[x]],ri=dfn[x];
update1(1,1,cnt);
x=F[cl[x]];
}
}
inline void REV(int x){
while(x){
le=dfn[cl[x]],ri=dfn[x];
update2(1,1,cnt);
x=F[cl[x]];
}
}
inline void solve(){
/*
for(int i=1;i<=cnt;i++){
cout<<i<<':'<<g[i].size()<<endl;
for(int j=g[i].size()-1;j>=0;j--) printf("%d ",g[i][j]);
puts("");
}
*/
memset(dfn,0,sizeof(dfn)),dc=0;
rp[1]=1,dfs1(1,0),dfs2(1,1);
build(1,1,cnt);
Q=read();
while(Q--){
ans=L=0,k=read();
for(int i=1;i<=k;i++){
now[i]=read();
if(!L) L=now[i]; else L=LCA(L,now[i]);
paint(now[i]);
}
Wt(ans-rp[F[L]]-k),puts("");
for(int i=1;i<=k;i++) REV(now[i]);
}
}
int main(){
// freopen("game.in","r",stdin);
// freopen("game.out","w",stdout);
// freopen("data.in","r",stdin);
// freopen("data.out","w",stdout);
int uu,vv;
T=read();
while(scanf("%d%d",&n,&m)==2){
init(),cnt=n;
for(int i=1;i<=m;i++){
uu=read(),vv=read();
add(uu,vv),add(vv,uu);
}
for(int i=1;i<=n;i++) if(!dfn[i]) dfs(i,-1);
solve();
}
return 0;
}