虚树的学习

虚树的学习

学习博客: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

D. Kingdom and its Cities

这个题目难点就是想到虚树,然后就是一个树形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 ;
}

posted @ 2020-07-17 16:32  EchoZQN  阅读(157)  评论(0编辑  收藏  举报