【BZOJ5329】【SDOI2018】战略游戏(圆方树,虚树)
【BZOJ5329】【SDOI2018】战略游戏(圆方树,虚树)
题面
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<= 210^5,
1<= q<= 10^5,
对于每组测试数据,有Sigma|S|<= 210^5
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
Sample Output
0
1
3
0
1
2
3
题解
首先把一般图构建出圆方树,
考虑若干个点的贡献,
显然是把他们连成一个最小的联通块,然后计算里面的圆点个数。
这样子答案就可以通过构建虚树计算出答案啦。
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<queue>
using namespace std;
#define ll long long
#define RG register
#define MAX 222222
inline int read()
{
RG int x=0,t=1;RG char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=-1,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return x*t;
}
int Tot,n,m;
struct Line{int v,next;};
struct Link
{
Line e[MAX<<2];
int h[MAX],cnt;
void init(){memset(h,0,sizeof(h));cnt=0;}
void Add(int u,int v)
{
e[++cnt]=(Line){v,h[u]};h[u]=cnt;
e[++cnt]=(Line){u,h[v]};h[v]=cnt;
}
}G,T;
namespace Graph
{
int dfn[MAX],low[MAX],tim,S[MAX],top;
void init(){memset(dfn,0,sizeof(dfn));memset(low,0,sizeof(low));tim=top=0;}
void Tarjan(int u)
{
dfn[u]=low[u]=++tim;S[++top]=u;
for(int i=G.h[u];i;i=G.e[i].next)
{
int v=G.e[i].v;
if(!dfn[v])
{
Tarjan(v);low[u]=min(low[u],low[v]);
if(low[v]>=dfn[u])
{
T.Add(++Tot,u);int x;
do{x=S[top--];T.Add(Tot,x);}while(v!=x);
}
}
else low[u]=min(low[u],dfn[v]);
}
}
}
namespace RST
{
int dfn[MAX],low[MAX],tim,fa[MAX],dep[MAX],top[MAX],size[MAX],hson[MAX],dis[MAX];
void init(){memset(hson,0,sizeof(hson));tim=0;}
void dfs1(int u,int ff)
{
fa[u]=ff;size[u]=1;dep[u]=dep[ff]+1;dis[u]=dis[ff]+(u<=n);
for(int i=T.h[u];i;i=T.e[i].next)
{
int v=T.e[i].v;if(v==ff)continue;
dfs1(v,u);size[u]+=size[v];
if(size[v]>size[hson[u]])hson[u]=v;
}
}
void dfs2(int u,int tp)
{
top[u]=tp;dfn[u]=++tim;
if(hson[u])dfs2(hson[u],tp);
for(int i=T.h[u];i;i=T.e[i].next)
if(T.e[i].v!=fa[u]&&T.e[i].v!=hson[u])
dfs2(T.e[i].v,T.e[i].v);
low[u]=tim;
}
int LCA(int u,int v)
{
while(top[u]^top[v])dep[top[u]]<dep[top[v]]?v=fa[top[v]]:u=fa[top[u]];
return dep[u]<dep[v]?u:v;
}
bool cmp(int a,int b){return dfn[a]<dfn[b];}
int p[MAX],S[MAX],K;
void Solve()
{
K=read();int ans=0,len=K;
for(int i=1;i<=K;++i)p[i]=read();
sort(&p[1],&p[K+1],cmp);
for(int i=K;i>1;--i)p[++K]=LCA(p[i],p[i-1]);
sort(&p[1],&p[K+1],cmp);K=unique(&p[1],&p[K+1])-p-1;
ans=p[1]<=n;
for(int i=1,tp=0;i<=K;++i)
{
while(tp&&low[S[tp]]<dfn[p[i]])--tp;
if(tp)ans+=dis[p[i]]-dis[S[tp]];S[++tp]=p[i];
}
printf("%d\n",ans-len);
}
}
int main()
{
int TCase=read();
while(TCase--)
{
Tot=n=read();m=read();G.init();T.init();
for(int i=1;i<=m;++i)G.Add(read(),read());
Graph::init();Graph::Tarjan(1);
RST::init();RST::dfs1(1,0);RST::dfs2(1,1);
int Q=read();
while(Q--)RST::Solve();
}
return 0;
}