传送门
容易发现,对于一颗确定的树而言,找到一个非叶子节点作为根节点,然后给每条边
e
d
g
e
i
,
j
(
d
e
p
[
j
]
>
d
e
p
[
i
]
)
edge_{i,j}(dep[j]>dep[i])
edgei,j(dep[j]>dep[i])赋一个权值
w
i
,
j
w_{i,j}
wi,j,设
c
n
t
i
cnt_i
cnti表示以
i
i
i为根节点的子树中的叶子节点个数,若
c
n
t
j
%
2
=
1
cnt_j\%2=1
cntj%2=1,那么
w
i
,
j
=
1
w_{i,j}=1
wi,j=1,若
c
n
t
j
%
2
=
=
0
cnt_{j}\%2==0
cntj%2==0,那么
w
i
,
j
=
2
w_{i,j}=2
wi,j=2,答案树上的所有边权之和。
先考虑无解情况,显然当叶子结点有奇数个的时候无解,其它情况均有解,容易知道当前叶子结点的个数的奇偶性。
现在考虑在树上的某些节点的上加一些叶子节点,有两种情况,一种是在非叶子节点上加叶子节点,那么从该非叶子节点到根节点的路径上的所有边权都会反转(即1变成2,2变成1),此外还多了一条边。第二种是在叶子节点上加一个叶子节点,与第一种的区别在于第一次加的时候不会发生到根节点路径上的边权反转,仅仅只是多了一条边。
知道上面这些后就不难得出本题的解法了,考虑树链剖分维护树上的区间和,加节点的操作其实就是区间的反转操作,打对应翻转懒标记即可,然后第二种情况需要特判一下才行。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5+5;
int fa[maxn],id[maxn],rk[maxn],top[maxn],son[maxn],tot=0,sz[maxn],
cnt[maxn],val[maxn],val2[maxn],t[maxn<<2],lz[maxn<<2],vis[maxn];
vector<int>g[maxn];
void dfs1(int u,int f){
fa[u]=f;
sz[u]=1;
cnt[u]=1;
bool fg=false;
for(int i=0;i<g[u].size();++i){
int v=g[u][i];
if(v==f)continue;
fg=true;
dfs1(v,u);
if(cnt[v]&1)val[v]=1;else val[v]=2;
sz[u]+=sz[v];
cnt[u]+=cnt[v];
if(sz[v]>sz[son[u]]){
son[u]=v;
}
}
cnt[u]-=fg;
}
void dfs2(int u,int f){
id[u]=++tot;
val2[tot]=val[u];
rk[tot]=u;
top[u]=f;
if(!son[u])return;
dfs2(son[u],f);
for(int i=0;i<g[u].size();++i){
int v=g[u][i];
if(v==fa[u] || v==son[u])continue;
dfs2(v,v);
}
}
void pushup(int rt){
t[rt]=t[rt<<1]+t[rt<<1|1];
}
void pushdown(int rt,int l,int r){
if(lz[rt]){
int mid=l+r>>1;
lz[rt<<1]^=1;lz[rt<<1|1]^=1;
t[rt<<1]=3*(mid-l+1)-t[rt<<1];
t[rt<<1|1]=3*(r-mid)-t[rt<<1|1];
lz[rt]=0;
}
}
void build(int rt,int l,int r){
if(l==r){
t[rt]=val2[l];
return;
}
int mid=l+r>>1;
build(rt<<1,l,mid);
build(rt<<1|1,mid+1,r);
pushup(rt);
}
void cg(int rt,int l,int r,int ql,int qr){
if(ql<=l && r<=qr){
lz[rt]^=1;
t[rt]=3*(r-l+1)-t[rt];
return;
}
pushdown(rt,l,r);
int mid=l+r>>1;
if(ql<=mid && qr>=l)cg(rt<<1,l,mid,ql,qr);
if(ql<=r && qr>=mid+1)cg(rt<<1|1,mid+1,r,ql,qr);
pushup(rt);
}
vector<int>rec;
void work(int u,int root){
while(top[u]!=root){
cg(1,1,tot,id[top[u]],id[u]);
u=fa[top[u]];
}
if(u!=root){
cg(1,1,tot,id[son[root]],id[u]);
}
}
int main()
{
int n,q;
scanf("%d%d",&n,&q);
int root=-1;
for(int i=0;i<n-1;++i){
int u,v;
scanf("%d%d",&u,&v);
g[u].push_back(v);
g[v].push_back(u);
if(g[u].size()>1)root=u;
if(g[v].size()>1)root=v;
}
dfs1(root,0);
dfs2(root,root);
build(1,1,tot);
while(q--){
int num=0;
scanf("%d",&num);
int ct=0;
rec.clear();
for(int i=0;i<num;++i){
int u;
scanf("%d",&u);
rec.push_back(u);
if((int)g[u].size()==1 && !vis[u])ct++;
vis[u]=1;
}
if((cnt[root]+num-ct)&1){
printf("-1\n");
for(int i=0;i<rec.size();++i)vis[rec[i]]=0;
continue;
}
for(int i=0;i<rec.size();++i)vis[rec[i]]=0;
for(int i=0;i<rec.size();++i){
if((int)g[rec[i]].size()==1 && !vis[rec[i]]){
vis[rec[i]]=1;
continue;
}
work(rec[i],root);
}
int ans=t[1]+num;
for(int i=0;i<rec.size();++i)vis[rec[i]]=0;
for(int i=0;i<rec.size();++i){
if((int)g[rec[i]].size()==1 && !vis[rec[i]]){
vis[rec[i]]=1;
continue;
}
work(rec[i],root);
}
for(int i=0;i<rec.size();++i)vis[rec[i]]=0;
printf("%d\n",ans);
}
}