【题意】
给出一个无向图,q次询问,每次给定一个点集s代表占领点,问有多少个未被占领的点可以作为点集s中两个点u,v的割点
【分析】
首先,先建立圆方树,问题转化为能包含 给定点集 的最小连通块的 圆点个数 - 占领点个数,也就是点集中两两点的并集的点个数-占领点个数
然后,按照圆方树的套路,我们要给点赋值,显然圆点赋1,方点赋0即可,然后把点权转移到父亲边的边权上去。
接着,我们需要利用类似虚树的套路,也算是一个小技巧,将询问点集按照dfs序排序,相邻两两简单路径长度之和+首尾简单路径长度(也就是围成一个圆,相邻两人的距离之和)除以二即可
那么最终答案为记得加上根节点(首尾的lca)的点权,这是点权转化为边权中丢失的部分
【代码】
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=2e5+5;
vector <int> G[maxn],YFT[maxn];
int n,m;
int dfn[maxn],low[maxn],dfs_time;
int in[maxn],st[maxn],top;
int sq;
void add(int x,int y)
{
YFT[x].push_back(y);
}
void tarjan(int u,int fa)
{
dfn[u]=low[u]=++dfs_time;
in[u]=1; st[++top]=u;
for(auto to:G[u])
{
if(to==fa || dfn[to]>dfn[u]) continue;
if(!dfn[to])
{
tarjan(to,u);
low[u]=min(low[u],low[to]);
}
else if(in[to]) {low[u]=min(low[u],dfn[to]); continue; }
if(low[to]==dfn[u])
{
sq++;
add(u,sq);
while(st[top]!=to)
{
in[st[top]]=0; add(sq,st[top]);
top--;
}
add(sq,to); in[to]=0; top--;
}
else if(low[to]>dfn[u])
{
add(u,to); top--; in[to]=0;
}
}
}
int dep[maxn],dis[maxn],rk;
int f[maxn][19];
void dfs(int u,int fa)
{
dfn[u]=++rk;
for(auto to:YFT[u])
{
// if(to==fa) continue;
dep[to]=dep[u]+1; dis[to]=dis[u]+(to<=n);
f[to][0]=u;
for(int i=1;i<=18;i++) f[to][i]=f[f[to][i-1]][i-1];
dfs(to,u);
}
}
int getlca(int x,int y)
{
if(dep[x]<dep[y]) swap(x,y);
for(int i=18;i>=0;i--)
if(dep[f[x][i]]>=dep[y])
x=f[x][i];
if(x==y) return x;
for(int i=18;i>=0;i--)
if(f[x][i]!=f[y][i])
x=f[x][i],y=f[y][i];
return f[x][0];
}
int getdis(int x,int y)
{
return dis[x]+dis[y]-2*dis[getlca(x,y)];
}
int qu[maxn];
bool cmp(int x,int y)
{
return dfn[x]<dfn[y];
}
int main()
{
// freopen("a.in","r",stdin);
// freopen("a.out","w",stdout);
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&m);
int x,y;
for(int i=1;i<=m;i++)
{
scanf("%d%d",&x,&y);
G[x].push_back(y); G[y].push_back(x);
}
for(int i=1;i<=n;i++) dfn[i]=low[i]=in[i]=0; top=dfs_time=0; sq=n;
tarjan(1,-1);
dep[1]=0,dis[1]=0,rk=0;
dfs(1,0);
int q;
scanf("%d",&q);
for(int i=1;i<=q;i++)
{
int num;
scanf("%d",&num);
for(int j=1;j<=num;j++) scanf("%d",&qu[j]);
sort(qu+1,qu+num+1,cmp);
int sum=getdis(qu[1],qu[num]);
for(int j=1;j<num;j++)
sum+=getdis(qu[j],qu[j+1]);
// printf("[%d] ",sum);
printf("%d\n",sum/2-num+(getlca(qu[1],qu[num])<=n));
}
for(int i=1;i<=n;i++) G[i].clear();
for(int i=1;i<=sq;i++) YFT[i].clear(),dfn[i]=0;
// return 0;
}
return 0;
}