正题
P6805
题目大意
给你一棵树,每次可选择两个叶子结点,然后覆盖路径上的边,代价为其长度,每个叶子结点只能选一次。
对于每次询问,加入若干新点(只会连接原树的点),问你覆盖所有边的最小代价。
解题思路
因为所有边都要覆盖,所以一个点至少连一条到父亲的边
不难发现,如果一个点的子树内有偶数各叶子结点,那么就要连出两条边,否则连一条边,因为所有点都要连向父亲,所以只计算多出来的
那么可以树链剖分,然后用线段树维护子树叶子结点的值
对于每次查询,相当于把若干链上的值异或1,然后查询整棵树的值
code
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
#define N 100100
using namespace std;
int n,m,q,x,y,w,tot,a[N],s[N],h[N],p[N];
int sz[N],fa[N],hs[N],deg[N],low[N],dfn[N],dep[N],top[N],lsz[N];
struct rec
{int to,nx;
}e[N<<1];
void add(int x,int y)
{e[++tot].to=y;e[tot].nx=h[x];h[x]=tot;return;
}
struct Tree
{#define ls x*2#define rs x*2+1int v[N<<2],lazy[N<<2];void push_up(int x){v[x]=v[ls]+v[rs];return;}void get(int x,int l,int r){lazy[x]^=1;v[x]=r-l+1-v[x];return;}void push_down(int x,int l,int r){if(lazy[x]){int mid=l+r>>1;get(ls,l,mid);get(rs,mid+1,r);lazy[x]=0;}return;}void change(int x,int L,int R,int l,int r){if(L==l&&R==r){get(x,L,R);return;}push_down(x,L,R);int mid=L+R>>1;if(r<=mid)change(ls,L,mid,l,r);else if(l>mid)change(rs,mid+1,R,l,r);else change(ls,L,mid,l,mid),change(rs,mid+1,R,mid+1,r);push_up(x);return;}int ask(int x,int L,int R,int l,int r){if(L==l&&R==r)return v[x];push_down(x,L,R);int mid=L+R>>1;if(r<=mid)return ask(ls,L,mid,l,r);else if(l>mid)return ask(rs,mid+1,R,l,r);else return ask(ls,L,mid,l,mid)+ask(rs,mid+1,R,mid+1,r);}
}T;
void dfs1(int x)
{sz[x]=1;if(deg[x]==1)lsz[x]=1,p[x]=1;for(int i=h[x];i;i=e[i].nx){int y=e[i].to;if(y==fa[x])continue;fa[y]=x;dep[y]=dep[x]+1;dfs1(y);sz[x]+=sz[y];lsz[x]+=lsz[y];if(sz[y]>sz[hs[x]])hs[x]=y;}return;
}
void dfs2(int x,int anc)
{dfn[x]=++w;top[x]=anc;if(!(lsz[x]&1))T.change(1,1,n,dfn[x],dfn[x]);if(hs[x])dfs2(hs[x],anc);for(int i=h[x];i;i=e[i].nx){int y=e[i].to;if(y==fa[x]||y==hs[x])continue;dfs2(y,y);}low[x]=w;return;
}
void add(int x)
{while(top[x]!=1){T.change(1,1,n,dfn[top[x]],dfn[x]);x=fa[top[x]];}T.change(1,1,n,dfn[1],dfn[x]);return;
}
int main()
{scanf("%d%d",&n,&q);for(int i=1;i<n;++i){scanf("%d%d",&x,&y);add(x,y);add(y,x);deg[x]++;deg[y]++;}dep[1]=fa[1]=1;dfs1(1);dfs2(1,1);while(q--){scanf("%d",&m);for(int i=1;i<=m;++i){scanf("%d",&a[i]);s[i]=1;}a[m+1]=0;sort(a+1,a+1+m);for(int i=1;i<=m;++i)if(a[i]==a[i+1])s[i+1]+=s[i];else if(p[a[i]]){if(!(s[i]&1))add(a[i]);//修改一条链}else if(s[i]&1)add(a[i]);if(!T.ask(1,1,n,dfn[1],dfn[1]))puts("-1");//无法两两匹配else printf("%d\n",T.ask(1,1,n,dfn[1],low[1])+n+m-2);//加上没算的边,1没有连向父亲的边,但两边都会多算一次for(int i=1;i<=m;++i)if(a[i]!=a[i+1]){if(p[a[i]]){if(!(s[i]&1))add(a[i]);}else if(s[i]&1)add(a[i]);}}return 0;
}