虚树,RE on #11
#include<bits/stdc++.h>
using namespace std;
const int N=5e5+5;
int n,q,k,ans;
int dep[N],sz[N],fa[N],top[N],son[N];//树剖数组
vector<int> e[N];//原图
vector<int> ee[N];//虚树图
int s[N],tp;//手写栈
int dfn[N],tot,a[N],val[N],flag;//虚树数组
void dfs1(int u,int f){
fa[u]=f;
sz[u]=1;
dep[u]=dep[f]+1;
dfn[u]=++tot;
for(int i=0;i<e[u].size();i++){
int v=e[u][i];
if(v==f) continue;
dfs1(v,u);
sz[u]+=sz[v];
if(sz[u]>sz[son[u]]) son[u]=v;
}
}
void dfs2(int u,int t){
top[u]=t;
for(int i=0;i<e[u].size();i++){
int v=e[u][i];
if(v==fa[u]) continue;
if(v==son[u]) dfs2(v,t);
else dfs2(v,v);
}
}
int LCA(int x,int y){
while(top[x]!=top[y]){
if(dep[top[x]]>dep[top[y]]) x=fa[top[x]];
else y=fa[top[y]];
}
return dep[x]<dep[y]?x:y;
}
bool cmp(int a1,int a2){
return dfn[a1]<dfn[a2];//按照dfs序顺序排列
}
void build(){//建立虚树
s[++tp]=1;//建立根
if(a[1]!=1) s[++tp]=a[1];//特判1号询问点是不是根
for(int i=2;i<=k;i++){//依次加入询问点
int lca=LCA(s[tp],a[i]);
while(tp>1 && dep[s[tp-1]]>=dep[lca]){//保证树根不被弹出,且弹出后的栈顶在lca以下
ee[s[tp-1]].push_back(s[tp]);//出栈连边
tp--;
}//现在剩下一个栈顶元素,对应深度大于等于lca
/*---------------------------*/
//讨论栈顶元素与lca的关系
if(lca!=s[tp]){
ee[lca].push_back(s[tp]);//lca向s[top]连边
s[tp]=lca;//lca替换栈顶元素
}
s[++tp]=a[i];//a[i]入栈
}
while(tp){//处理最后一条链
ee[s[tp-1]].push_back(s[tp]);
tp--;
}
}
void check(int u){
if(val[u]&&val[fa[u]]) flag=1;
for(int i=0;i<ee[u].size();i++){
int v=ee[u][i];
check(v);
}
}
void rebuild(int u){
val[u]=0;
for(int i=0;i<ee[u].size();i++){
int v=ee[u][i];
rebuild(v);
}
ee[u].clear();
}
void DP(int u){
if(val[u]){//是查询点
for(int i=0;i<ee[u].size();i++){
int v=ee[u][i];
DP(v);
if(val[v]>=1){//子节点有查询点,说明要断掉u->v的路径上的点
ans++;
val[v]=0;//把标记归零
}
}
}else{//不是查询点
for(int i=0;i<ee[i].size();i++){
int v=ee[u][i];
DP(v);
val[u]+=val[v];
val[v]=0;
}
if(val[u]>=2){//超过两个点相连,说明这个点要删除
ans++;
val[u]=0;
}
}
ee[u].clear();//摧毁虚树
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
cin>>n;
for(int i=1;i<n;i++){
int a,b;
cin>>a>>b;
e[a].push_back(b);
e[b].push_back(a);
}
dfs1(1,0);
dfs2(1,1);//预处理树剖求LCA
cin>>q;
for(int cas=1;cas<=q;cas++){
cin>>k;
for(int i=1;i<=k;i++) cin>>a[i];
/*-----------------------------------*/
sort(a+1,a+1+k,cmp);//dfn序排序
val[1]=0;
build();//建虚树
/*-----------------------------------*/
ans=0;
for(int i=1;i<=k;i++){
val[a[i]]=1;//让所有查询点权值为1
}
/*-----------------------------------*/
flag=0;
check(1);
if(flag==1){
cout<<-1<<endl;
rebuild(1);
continue;
}
/*-----------------------------------*/
DP(1);
cout<<ans<<"\n";
}
return 0;
}
(为了防止巨佬们嫌弃我丑陋的马蜂,我写上了注释,求调啊 T^T)