虚树的学习
虚树的学习
学习博客:https://www.cnblogs.com/chenhuan001/p/5639482.html
模板题目:https://darkbzoj.tk/problem/2286
模板:https://www.cnblogs.com/Orz-IE/p/12149366.html
题目博客:https://blog.csdn.net/ez_2016gdgzoi471/article/details/78844732
这个题目难点就是想到虚树,然后就是一个树形dp \(dp[i][j]\)
- \(j==1\) 表示有点从 \(i\) 这棵子树出来
- \(j==0\) 表示没有点从 \(i\) 这棵子树出来
#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
#define debug(x) printf("debug:%s=%d\n",#x,x);
//#define debug(x) cout << #x << ": " << x << endl
//快读,需要文件读入
# define getchar() (S==T&&(T=(S=BB)+fread(BB,1,1<<20,stdin),S==T)?EOF:*S++)
char BB[1 << 20], *S = BB, *T = BB;
int read()
{
int x=0;
char c=getchar();
while (!isdigit(c)) c=getchar();
while (isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=getchar();
return x;
}
using namespace std;
const int maxn = 1e5+10;
typedef long long ll;
int head[maxn<<1],nxt[maxn<<1],to[maxn<<1],cnt;
void ADD(int u,int v){
// printf("u=%d v=%d\n",u,v);
++cnt,to[cnt]=v,nxt[cnt]=head[u],head[u]=cnt;
++cnt,to[cnt]=u,nxt[cnt]=head[v],head[v]=cnt;
}
int dfn[maxn],dep[maxn],siz[maxn],fa[maxn],top[maxn],son[maxn],tim;
void dfs1(int u){
siz[u]=1;
dep[u]=dep[fa[u]]+1;
for(int i=head[u];i;i=nxt[i]){
int v = to[i];
if(v==fa[u]) continue;
fa[v]=u;
dfs1(v);
siz[u]+=siz[v];
if(!son[u]||siz[son[u]]<siz[v]) son[u]=v;
}
}
void dfs2(int u,int tp){
dfn[u]=++tim,top[u]=tp;
if(!son[u]) return ;
dfs2(son[u],tp);
for(int i=head[u];i;i=nxt[i]){
int v = to[i];
if(v==fa[u]||v==son[u]) continue;
dfs2(v,v);
}
}
int Lca(int u,int v) {
while (top[u] != top[v]) {
if (dep[top[u]] < dep[top[v]]) swap(u, v);
u = fa[top[u]];
}
return dep[u] > dep[v] ? v : u;
}
int stk[maxn],cur;
void insert(int p){
// printf("p=%d\n",p);
int x = stk[cur];
if(x==1){stk[++cur]=p;return;}
int lca = Lca(p,x);
while(lca!=stk[cur]){
int y = stk[--cur];
if(dfn[y]<dfn[lca]){
ADD(lca,x),stk[++cur]=lca;
break;
}
ADD(x,y),x=stk[cur];
}
stk[++cur]=p;
}
int c[maxn],have[maxn];
bool cmp(const int& a,const int& b){
return dfn[a]<dfn[b];
}
ll dp[maxn][2],a[maxn];
void DP(int u,int pre){
siz[u]=have[u];
if(have[u]) dp[u][0]=inf,dp[u][1]=0;
else dp[u][0]=0,dp[u][1]=0;
for(int i=head[u];i;i=nxt[i]){
int v = to[i];
if(v==pre) continue;
DP(v,u);
}
if(have[u]){
for(int i=head[u];i;i=nxt[i]){
int v = to[i];
if(v==pre) continue;
if(siz[v]&&dep[v]-dep[u]>1) dp[u][1]+=dp[v][1]+1;
else dp[u][1]+=dp[v][0];
}
}
else{
int now=0;
ll ans1=0,ans2=0;
for(int i=head[u];i;i=nxt[i]){
int v = to[i];
if(v==pre) continue;
ans2+=dp[v][0];
ans1+=min(dp[v][0],dp[v][1]);
a[++now]=dp[v][1]-dp[v][0];
}
dp[u][0]=min(ans2,ans1+1);
sort(a+1,a+1+now);
dp[u][1]+=ans2+a[1];
}
have[u]=head[u]=0;
}
void solve(){
int k = read();
// debug(k);
cnt=0,stk[cur=1]=1;
for(int i=1;i<=k;i++) c[i]=read(),have[c[i]]=1;
sort(c+1,c+1+k,cmp);
if(c[1]!=1) stk[++cur]=c[1];
for(int i=2;i<=k;i++) insert(c[i]);
while(--cur) ADD(stk[cur],stk[cur+1]);
DP(1,0);
ll ans = min(dp[1][0],dp[1][1]);
if(ans>=inf) printf("-1\n");
else printf("%lld\n",ans);
return ;
}
int main() {
// freopen("1.in", "r", stdin);
int n = read();
for (int i = 1; i < n; i++) {
int u = read(), v = read();
ADD(u, v);
}
dfs1(1),dfs2(1,1);
memset(head,0,sizeof(head));
int m = read();
while (m--) solve();
return 0;
}
附录:
虚树还是很简单的,但是要找到自己喜欢习惯的码风板子。
typedef long long ll;
int head[maxn<<1],nxt[maxn<<1],to[maxn<<1],cnt;
void ADD(int u,int v){
// printf("u=%d v=%d\n",u,v);
++cnt,to[cnt]=v,nxt[cnt]=head[u],head[u]=cnt;
++cnt,to[cnt]=u,nxt[cnt]=head[v],head[v]=cnt;
}
int dfn[maxn],dep[maxn],siz[maxn],fa[maxn],top[maxn],son[maxn],tim;
void dfs1(int u){
siz[u]=1;
dep[u]=dep[fa[u]]+1;
for(int i=head[u];i;i=nxt[i]){
int v = to[i];
if(v==fa[u]) continue;
fa[v]=u;
dfs1(v);
siz[u]+=siz[v];
if(!son[u]||siz[son[u]]<siz[v]) son[u]=v;
}
}
void dfs2(int u,int tp){
dfn[u]=++tim,top[u]=tp;
if(!son[u]) return ;
dfs2(son[u],tp);
for(int i=head[u];i;i=nxt[i]){
int v = to[i];
if(v==fa[u]||v==son[u]) continue;
dfs2(v,v);
}
}
int Lca(int u,int v) {
while (top[u] != top[v]) {
if (dep[top[u]] < dep[top[v]]) swap(u, v);
u = fa[top[u]];
}
return dep[u] > dep[v] ? v : u;
}
int stk[maxn],cur;
void insert(int p){
// printf("p=%d\n",p);
int x = stk[cur];
if(x==1){stk[++cur]=p;return;}
int lca = Lca(p,x);
while(lca!=stk[cur]){
int y = stk[--cur];
if(dfn[y]<dfn[lca]){
ADD(lca,x),stk[++cur]=lca;
break;
}
ADD(x,y),x=stk[cur];
}
stk[++cur]=p;
}
int c[maxn],have[maxn];
bool cmp(const int& a,const int& b){
return dfn[a]<dfn[b];
}
ll dp[maxn][2],a[maxn];
void DP(int u,int pre){
}
void solve(){
int k = read();
// debug(k);
cnt=0,stk[cur=1]=1;
for(int i=1;i<=k;i++) c[i]=read(),have[c[i]]=1;
sort(c+1,c+1+k,cmp);
if(c[1]!=1) stk[++cur]=c[1];
for(int i=2;i<=k;i++) insert(c[i]);
while(--cur) ADD(stk[cur],stk[cur+1]);
DP(1,0);
ll ans = min(dp[1][0],dp[1][1]);
if(ans>=inf) printf("-1\n");
else printf("%lld\n",ans);
return ;
}