看到这样的性质就可以想到圆方树,而两点之间贡献的答案就是路径上的圆点数量,为了去重,我们只需要将虚树建出来求路径上的点权和就行了,实际上并不需要建出来,可以发现按照dfs序排一下之后就可以考虑欧拉回路,只需要将每个点的深度加起来-相邻两点的lca深度即可,这里的深度是带权深度,相当于点到根的圆点数量,最后这个虚树的权值也并不是正确的,因为在所有点的lca的父亲到根节点的圆点实际上并不会贡献答案,减一下就可以了.
#include
using namespace std;
const int N=400010;
struct edge{
int y,nex;
}s[N<<1],e[N];
int first[N],fir[N],len,le,n,m,tim,q,c;
int dfn[N],low[N],dep[N],fa[N][20],sz[N],a[N];
int sta[N],top,num;
void ins(int x,int y){s[++len]=(edge){y,first[x]};first[x]=len;}
void inss(int x,int y){e[++le]=(edge){y,fir[x]};fir[x]=le;}
bool cmp(const int&x,const int&y){return dfn[x]=dfn[s[i].y]) inss(num,sta[top]),top--;
inss(x,num);
}
}
else low[x]=min(low[x],dfn[s[i].y]);
}
}
void dfs(int x){
dfn[x]=++tim;
for(int k=1;k<=19;k++) fa[x][k]=fa[fa[x][k-1]][k-1];
for(int i=fir[x];i;i=e[i].nex){
fa[e[i].y][0]=x;
dep[e[i].y]=dep[x]+1;
sz[e[i].y]=sz[x]+(e[i].y<=n);
dfs(e[i].y);
}
}
int lca(int x,int y){
if(dep[x]>=dep[y]) swap(x,y);
for(int k=19;k>=0;k--) if(dep[fa[y][k]]>=dep[x]) y=fa[y][k];
if(x==y) return x;
for(int k=19;k>=0;k--) if(fa[x][k]!=fa[y][k]) x=fa[x][k],y=fa[y][k];
return fa[x][0];
}
int main(){
int T,x,y;
long long ans;
scanf("%d",&T);
while(T--){
prepare();
scanf("%d %d",&n,&m);num=n;
for(int i=1;i<=m;i++) scanf("%d %d",&x,&y),ins(x,y),ins(y,x);
Tarjan(1);dep[1]=sz[1]=1;tim=0;dfs(1);
scanf("%d",&q);
while(q--){
scanf("%d",&c);
for(int i=1;i<=c;i++) scanf("%d",&a[i]);
sort(a+1,a+1+c,cmp);
ans=sz[a[1]]-sz[fa[lca(a[1],a[c])][0]];
for(int i=2;i<=c;i++) ans+=sz[a[i]]-sz[lca(a[i-1],a[i])];
printf("%lld\n",ans-c);
}
}
}