测试地址:战略游戏
做法:本题需要用到圆方树+虚树。
显而易见的是,两个点之间路径的必经点,就等于它们之间路径上的所有割点。因此我们很快想到建出圆方树,这样两点间路径上所有的圆点(除去两端)就是对应的割点。而询问一个集合,问能切开集合中某两个点的所有点,那就是求所有这些点两两之间路径的并,答案就等于并集中圆点的数目减去询问集合的大小(因为询问集合内的点不能算作答案)。
树上一个点集两两之间路径的并也是一棵树,用更加熟悉的一个词来说,是一棵虚树,因此我们在圆方树上求虚树即可,时间复杂度为。
以下是本人代码:
#include <bits/stdc++.h>
using namespace std;
const int N=200010;
int T,n,m,q;
int first[N],firstt[N],tot,totpbc;
int low[N],dfn[N],st[N],top,tim,pos[N],p[N<<1],s;
int fa[N][20],dep[N],dis[N];
bool vis[N];
struct edge
{
int v,next;
}e[N<<1],t[N<<1];
void insert(int a,int b)
{
e[++tot].v=b;
e[tot].next=first[a];
first[a]=tot;
}
void insertt(int a,int b)
{
t[++tot].v=b;
t[tot].next=firstt[a];
firstt[a]=tot;
}
void tarjan(int v,int fa)
{
low[v]=dfn[v]=++tim;
st[++top]=v;
vis[v]=1;
for(int i=first[v];i;i=e[i].next)
if (e[i].v!=fa)
{
if (!vis[e[i].v])
{
tarjan(e[i].v,v);
low[v]=min(low[v],low[e[i].v]);
if (low[e[i].v]>=dfn[v])
{
++totpbc;
insertt(v,totpbc);
do
{
insertt(totpbc,st[top]);
}while(st[top--]!=e[i].v);
}
}
else low[v]=min(low[v],dfn[e[i].v]);
}
}
void dfs(int v)
{
pos[v]=++tim;
for(int i=firstt[v];i;i=t[i].next)
{
fa[t[i].v][0]=v;
dep[t[i].v]=dep[v]+1;
dis[t[i].v]=dis[v]+(t[i].v<=n);
dfs(t[i].v);
}
}
bool cmp(int a,int b)
{
return pos[a]<pos[b];
}
int lca(int x,int y)
{
if (dep[x]<dep[y]) swap(x,y);
for(int i=18;i>=0;i--)
if (dep[fa[x][i]]>=dep[y]) x=fa[x][i];
if (x==y) return x;
for(int i=18;i>=0;i--)
if (fa[x][i]!=fa[y][i])
x=fa[x][i],y=fa[y][i];
return fa[x][0];
}
void build()
{
sort(p+1,p+s+1,cmp);
for(int i=1;i<s;i++)
p[s+i]=lca(p[i],p[i+1]);
sort(p+1,p+(s<<1),cmp);
int tot=1;
for(int i=1;i<(s<<1)-1;i++)
if (p[i]!=p[i+1]) p[++tot]=p[i+1];
int ans;
top=ans=0;
for(int i=1;i<=tot;i++)
{
while(top&&lca(st[top],p[i])!=st[top]) top--;
st[++top]=p[i];
if (top>1) ans+=dis[st[top]]-dis[st[top-1]];
}
if (p[1]<=n) ans++;
printf("%d
",ans-s);
}
int main()
{
scanf("%d",&T);
while(T--)
{
memset(first,0,sizeof(first));
memset(firstt,0,sizeof(firstt));
tot=0;
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
int a,b;
scanf("%d%d",&a,&b);
insert(a,b),insert(b,a);
}
tot=top=tim=0,totpbc=n;
memset(vis,0,sizeof(vis));
tarjan(1,0);
memset(fa,0,sizeof(fa));
tim=0;
dis[1]=1,dep[1]=0,dep[0]=-1;
dfs(1);
for(int i=1;i<=18;i++)
for(int j=1;j<=totpbc;j++)
fa[j][i]=fa[fa[j][i-1]][i-1];
scanf("%d",&q);
for(int i=1;i<=q;i++)
{
scanf("%d",&s);
for(int j=1;j<=s;j++)
scanf("%d",&p[j]);
build();
}
}
return 0;
}