P4606 [SDOI2018]战略游戏 圆方树+虚树
题意:
分析:
- 前置芝士:圆方树,虚树
题意让我们求出,哪些点删掉后会使得点集里某两个点不再联通
暴力 \(O(qn^2)\) , 就是每次询问直接枚举点, \(O(n)\) check 一下是否合法
正解:
对于这种图上割点 的问题,我们可以建出圆方树转化在树上去做,我们建出圆方树,利用圆方树的性质:原图上任意两点的割点一定是他们在圆方树上路径上的原点,所以我们只要求出在点集中某两个点的路径上的原点的个数,对于这种给定点集总和的题,我们按照常见思路建出虚树,直接求一下虚树的大小
tip: 对于求虚树大小的这种操作,其实都不需要建出虚树,加边的时候直接统计新加进来多少个节点
代码:
#include<bits/stdc++.h>
#define pii pair<int,int>
#define mk(x,y) make_pair(x,y)
#define lc rt<<1
#define rc rt<<1|1
#define pb push_back
#define fir first
#define sec second
#define inl inline
#define reg register
using namespace std;
namespace zzc
{
inline int read()
{
int x=0,f=1;char ch=getchar();
while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
return x*f;
}
const int maxn = 4e5+5;
const int maxm = 8e5+5;
int t,n,m,qt,top,col,ans,idx;
int dfn[maxn],st[maxn],low[maxn],dep[maxn],fa[maxn][20],tmp[maxn];
struct graph
{
int cnt;
int head[maxn];
struct edge
{
int to,nxt;
}e[maxm];
void clear()
{
cnt=0;
memset(head,0,sizeof(head));
}
void add(int u,int v)
{
e[++cnt].to=v;
e[cnt].nxt=head[u];
head[u]=cnt;
}
void add_edge(int u,int v)
{
add(u,v);add(v,u);
}
}t1,t2;
void tarjan(int u,int ff)
{
dfn[u]=low[u]=++idx;
st[++top]=u;
for(int i=t1.head[u];i;i=t1.e[i].nxt)
{
int v=t1.e[i].to;
if(v==ff) continue;
if(!dfn[v])
{
tarjan(v,u);
low[u]=min(low[u],low[v]);
if(low[v]>=dfn[u])
{
t2.add_edge(++col,u);
for(;st[top+1]!=v;top--) t2.add_edge(col,st[top]);
}
}
else low[u]=min(low[u],dfn[v]);
}
}
void dfs(int u,int ff)
{
dfn[u]=++idx;fa[u][0]=ff;dep[u]=dep[ff]+1;
for(int i=t2.head[u];i;i=t2.e[i].nxt)
{
int v=t2.e[i].to;
if(v==ff) continue;
dfs(v,u);
}
}
int lca(int x,int y)
{
if(dep[x]<dep[y]) swap(x,y);
for(int i=19;i>=0;i--) if(dep[fa[x][i]]>=dep[y]) x=fa[x][i];
if(x==y) return x;
for(int i=19;i>=0;i--) if(fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
return fa[x][0];
}
bool cmp(int x,int y)
{
return dfn[x]<dfn[y];
}
void work()
{
int a,b;
t=read();
while(t--)
{
t1.clear();t2.clear();for(int i=1;i<=col;i++) dfn[i]=0;
n=read();m=read();col=n;
for(int i=1;i<=m;i++)
{
a=read();b=read();
t1.add_edge(a,b);
}
top=0;idx=0;tarjan(1,0);
idx=0;dfs(1,0);
for(int j=1;j<=19;j++) for(int i=1;i<=col;i++) fa[i][j]=fa[fa[i][j-1]][j-1];
qt=read();
while(qt--)
{
a=read();ans=0;top=0;
for(int i=1;i<=a;i++) tmp[i]=read();
sort(tmp+1,tmp+a+1,cmp);
for(int i=1;i<=a;i++)
{
if(!top) st[++top]=tmp[i];
else
{
int x=lca(st[top],tmp[i]);
ans+=((dep[st[top]]+1)>>1)-((dep[x]+1)>>1);
while(top&&dep[st[top]]>=dep[x]) top--;
st[++top]=x;st[++top]=tmp[i];
}
}
ans+=((dep[st[top]]+1)>>1)-(dep[st[1]]>>1)-a;
printf("%d\n",ans);
}
}
}
}
int main()
{
// freopen("01.in","r",stdin);
// freopen("ans.out","w",stdout);
zzc::work();
return 0;
}