虚树

虚树

讲解

模板

点击查看代码

struct Tree{        //原树
    int tot,head[maxn],nx[maxn],to[maxn];
    ll w[maxn];
    void add(int x,int y,ll z){
        to[++tot]=y;nx[tot]=head[x];head[x]=tot;
        w[tot]=z;
    }

    int cnt,dfn[maxn],f[maxn][21],dep[maxn];
    void dfs(int x,int fa){
        dfn[x]=++cnt;dep[x]=dep[fa]+1;
        for(int i=1;i<=20;i++)f[x][i]=f[f[x][i-1]][i-1];
        for(int i=head[x];i;i=nx[i]){
            int v=to[i];if(v==fa)continue;
            f[v][0]=x;
            dfs(v,x);
        }
        return ;
    }

    int lca(int x,int y){
        if(dep[x]<dep[y])swap(x,y);
        for(int i=20;i>=0;i--){
            if(dep[f[x][i]]>=dep[y])x=f[x][i];
        }
        if(x==y)return x;
        for(int i=20;i>=0;i--){
            if(f[x][i]!=f[y][i]){
                x=f[x][i];
                y=f[y][i];
            }
        }
        return f[x][0];
    }

}T;

struct Vtree{
    int tot,head[maxn],nx[maxn],to[maxn];
    // ll w[maxn];      根据题目需要
    void add(int x,int y){
        to[++tot]=y;nx[tot]=head[x];head[x]=tot;
        to[++tot]=x;nx[tot]=head[y];head[y]=tot;
       // w[tot]=z;
    }

    int h[maxn],k;    //储存关键点
    int st[maxn],top;   //用单调栈来建虚树

    /*
    其他信息

    */


    void build(){

        sort(h+1,h+k+1,[](int i,int j){
            return T.dfn[i]<T.dfn[j];
        });
        st[top=1]=1;
        for(int i=1;i<=k;i++){
            s.insert(h[i]);
            if(h[i]!=1){
                int l=T.lca(h[i],st[top]);
                if(l!=st[top]){
                    while(T.dfn[l]<T.dfn[st[top-1]]){
                        add(st[top],st[top-1]);
                        top--;
                    }
                    if(T.dfn[l]>T.dfn[st[top-1]]){
                        add(l,st[top]);
                        st[top]=l;
                    }
                    else add(l,st[top--]);
                }
                st[++top]=h[i];
            }
        }
        for(int i=1;i<top;i++)add(st[i],st[i+1]);
        return ;
    }

//以下根据题目修改
    void init(){
        tot=0;
        /*

        */
    }

    void solve(){
        /*

        */
        build();

        /*

        */
        init();
    }
}V;

例题

1. [SDOI2011] 消耗战
首先考虑普通dp
\(dp_u\)表示以u为根的子树中,割掉所有给定点的最小代价
1.若u不是给定点
\(dp_u=min(minn[u],\sum_{v\in S}dp_v)\)
\(minn[u]\)为根到\(u\)路径边权值的最小值
\(S\)\(u\)的相邻点的集合

2.若u是给定点
\(dp_u=minn[u]\)

复杂度\(O(nm)\),这样肯定会超时
构建虚树,在虚树上dp即可

点击查看代码
#include<functional>
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<complex>
#include<string>
#include<cstdio>
#include<vector>
#include<cmath>
#include<queue>
#include<deque>
#include<stack>
#include<map>
#include<set>
#define ll long long 
#define pa pair<int,int>
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define YES {puts("YES");return;}
#define NO {puts("NO");return ;}
using namespace std;
const int maxn=5e5+101;
const int MOD=1e9+7;
const ll inf=9223372036854775807;
const double eps=1e-12;

ll read(){
    ll x=0,f=1;char ch=getchar();
    for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
    for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';
    return x*f;
}
struct Tree{
    int tot,head[maxn],nx[maxn],to[maxn];
    ll w[maxn];
    void add(int x,int y,ll z){
        to[++tot]=y;nx[tot]=head[x];head[x]=tot;
        w[tot]=z;
    }

    int cnt,dfn[maxn],f[maxn][21],dep[maxn];
    ll minn[maxn];
    void dfs(int x,int fa){
        dfn[x]=++cnt;dep[x]=dep[fa]+1;
        for(int i=1;i<=20;i++){
            f[x][i]=f[f[x][i-1]][i-1];
        }
        for(int i=head[x];i;i=nx[i]){
            int v=to[i];if(v==fa)continue;
            f[v][0]=x;minn[v]=min(minn[x],w[i]);
            dfs(v,x);
        }
        return ;
    }

    int lca(int x,int y){
        if(dep[x]<dep[y])swap(x,y);
        for(int i=20;i>=0;i--){
            if(dep[f[x][i]]>=dep[y])x=f[x][i];
        }
        if(x==y)return x;
        for(int i=20;i>=0;i--){
            if(f[x][i]!=f[y][i]){
                x=f[x][i];
                y=f[y][i];
            }
        }
        return f[x][0];
    }

}T;

struct Vtree{
    int tot,head[maxn],nx[maxn],to[maxn];
    ll w[maxn];
    void add(int x,int y){
        to[++tot]=y;nx[tot]=head[x];head[x]=tot;
        to[++tot]=x;nx[tot]=head[y];head[y]=tot;
       // w[tot]=z;
    }

    int h[maxn],k;    //储存关键点
    int st[maxn],top;
    set<int>s;  //关键点的集合
    void build(){
        sort(h+1,h+k+1,[](int i,int j){
            return T.dfn[i]<T.dfn[j];
        });
        st[top=1]=1;
        for(int i=1;i<=k;i++){
            s.insert(h[i]);
            if(h[i]!=1){
                int l=T.lca(h[i],st[top]);
                if(l!=st[top]){
                    while(T.dfn[l]<T.dfn[st[top-1]]){
                        add(st[top],st[top-1]);
                        top--;
                    }
                    if(T.dfn[l]>T.dfn[st[top-1]]){
                        add(l,st[top]);
                        st[top]=l;

                    }
                    else{
                        add(l,st[top--]);
                    }
                }
                st[++top]=h[i];
            }
        }
        for(int i=1;i<top;i++){
            add(st[i],st[i+1]);
        }
        return ;
    }

    void init(){
        tot=0;
        s.clear();
    }

    ll dp(int u,int fa){
        ll ans=inf;
        for(int i=head[u];i;i=nx[i]){
            int v=to[i];if(v==fa)continue;
            if(ans==inf)ans=dp(v,u);
            else ans+=dp(v,u);
        }
        head[u]=0;
        if(s.count(u))return T.minn[u];
        else return min(T.minn[u],ans);
    }

    void solve(){
        k=read();
        for(int i=1;i<=k;i++)h[i]=read();
        build();
        printf("%lld\n",dp(1,0));
        init();
    }
}V;

int n,m;
int main(){
    n=read();
    for(int i=1;i<n;i++){
        int x=read(),y=read(),z=read();
        T.add(x,y,z);T.add(y,x,z);
    }
    T.minn[1]=inf;T.dfs(1,0);
    m=read();
    while(m--)V.solve();
    return 0;
}

2.[HEOI2014]大工程

点击查看代码
#include<functional>
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<complex>
#include<string>
#include<cstdio>
#include<vector>
#include<cmath>
#include<queue>
#include<deque>
#include<stack>
#include<map>
#include<set>
#define ll long long 
#define pa pair<int,int>
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define YES {puts("YES");return;}
#define NO {puts("NO");return ;}
using namespace std;
const int maxn=2e6+101;
const int MOD=1e9+7;
const ll inf=2147483647;
const double eps=1e-12;

ll read(){
    ll x=0,f=1;char ch=getchar();
    for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
    for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';
    return x*f;
}
struct Tree{
    int tot,head[maxn],nx[maxn],to[maxn];
    void add(int x,int y){
        to[++tot]=y;nx[tot]=head[x];head[x]=tot;
    }

    int cnt,dfn[maxn],f[maxn][21],dep[maxn];
    void dfs(int x,int fa){
        dfn[x]=++cnt;dep[x]=dep[fa]+1;
        for(int i=1;i<=20;i++)f[x][i]=f[f[x][i-1]][i-1];

        for(int i=head[x];i;i=nx[i]){
            int v=to[i];if(v==fa)continue;
            f[v][0]=x;
            dfs(v,x);
        }
        return ;
    }

    int lca(int x,int y){
        if(dep[x]<dep[y])swap(x,y);
        for(int i=20;i>=0;i--){
            if(dep[f[x][i]]>=dep[y])x=f[x][i];
        }
        if(x==y)return x;
        for(int i=20;i>=0;i--){
            if(f[x][i]!=f[y][i]){
                x=f[x][i];
                y=f[y][i];
            }
        }
        return f[x][0];
    }

    int getdis(int x,int y){
        return dep[x]+dep[y]-2*dep[lca(x,y)];
    }

}T;

struct Vtree{
    int tot,head[maxn],nx[maxn],to[maxn];
    void add(int x,int y){
        to[++tot]=y;nx[tot]=head[x];head[x]=tot;
        to[++tot]=x;nx[tot]=head[y];head[y]=tot;
    }

    int h[maxn],k;    //储存关键点
    int st[maxn],top;
    set<int>s;  //关键点的集合
    void build(){
        sort(h+1,h+k+1,[](int i,int j){
            return T.dfn[i]<T.dfn[j];
        });
        st[top=1]=1;
        for(int i=1;i<=k;i++){
            s.insert(h[i]);
            if(h[i]!=1){
                int l=T.lca(h[i],st[top]);
                if(l!=st[top]){
                    while(T.dfn[l]<T.dfn[st[top-1]]){
                        add(st[top],st[top-1]);
                        top--;
                    }
                    if(T.dfn[l]>T.dfn[st[top-1]]){
                        add(l,st[top]);
                        st[top]=l;

                    }
                    else add(l,st[top--]);
                }
                st[++top]=h[i];
            }
        }
        for(int i=1;i<top;i++)add(st[i],st[i+1]);
        return ;
    }


    ll al;
    int deep[maxn],low[maxn];
    void init(){
        tot=0;al=0;
        s.clear();
    }
    ll sz[maxn];
    void dfs(int u,int fa){
        sz[u]=0;
        if(s.count(u))sz[u]=1;
        for(int i=head[u];i;i=nx[i]){
            int v=to[i];if(v==fa)continue;
            dfs(v,u);sz[u]+=sz[v];
        }
        return ;
    }   
    pa dp(int u,int fa){
        int maxx=-1,minn=inf;//最大值,最小值
        int getde=-1,getlow=inf;  //最大深度,最小深度
        if(s.count(u))getde=getlow=0;
        deep[u]=0,low[u]=0;
        for(int i=head[u];i;i=nx[i]){
            int v=to[i];if(v==fa)continue;
            pa now=dp(v,u);
            maxx=max(now.fi,maxx);
            minn=min(now.se,minn);
            
            ll w=T.getdis(u,v);
            deep[v]+=w;low[v]+=w;
            if(getde==-1){
                getde=getlow=deep[v];
                if(s.count(u))maxx=minn=deep[v];
            }
            else {
                maxx=max(maxx,getde+deep[v]);
                minn=min(minn,getlow+low[v]);
            }
            getde=max(getde,deep[v]);
            getlow=min(getlow,low[v]);
            
            al+=w*(k-sz[v])*sz[v];
        }
        deep[u]=getde;low[u]=getlow;
        head[u]=0;
        return mp(maxx,minn);
    }

    void solve(){
        k=read();
        for(int i=1;i<=k;i++)h[i]=read();
        build();
        dfs(1,0);
        pa ans=dp(1,0);
        printf("%lld %d %d\n",al,ans.se,ans.fi);
        init();
    }
}V;

int n,m;
int main(){
    n=read();
    for(int i=1;i<n;i++){
        int x=read(),y=read();
        T.add(x,y);T.add(y,x);
    }
    T.dfs(1,0);
    m=read();
    while(m--)V.solve();
    return 0;
}

3.D. Kingdom and its Cities
题意:n个点的树,q次询问,每次询问给出k个关键点的集合,问是否能删去一些非关键点,使得所有关键点不连通,若可行输出最小删点个数
题解:
无解情况:若存在一个点和它的父亲都是关键点,那么输出-1
有解:
不难发现,删去的点都属于k个关键点两两lca的集合,并且总共k很小
建立虚树,选择虚树上的贪心
若虚树上一个非关键点有多个分支有多个关键点,那么这个点删去,并且向上传递当前子树0个关键点
因为当前非关键点的子树已经保证不连通了

若虚树上一个关键点连着的一个或者多个分支有关键点,那么要删去有关键点的分支个数
因为建立虚树时,若两个在原树相邻关键点,在虚树上直接相连,需要单独考虑

点击查看代码
#include<functional>
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<complex>
#include<string>
#include<cstdio>
#include<vector>
#include<cmath>
#include<queue>
#include<deque>
#include<stack>
#include<map>
#include<set>
#define ll long long 
#define pa pair<int,int>
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define YES {puts("YES");return;}
#define NO {puts("NO");return ;}
using namespace std;
const int maxn=2e6+101;
const int MOD=1e9+7;
const ll inf=2147483647;
const double eps=1e-12;

ll read(){
    ll x=0,f=1;char ch=getchar();
    for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
    for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';
    return x*f;
}
struct Tree{
    int tot,head[maxn],nx[maxn],to[maxn];
    void add(int x,int y){
        to[++tot]=y;nx[tot]=head[x];head[x]=tot;
    }

    int cnt,dfn[maxn],f[maxn][21],dep[maxn];
    void dfs(int x,int fa){
        dfn[x]=++cnt;dep[x]=dep[fa]+1;
        for(int i=1;i<=20;i++)f[x][i]=f[f[x][i-1]][i-1];

        for(int i=head[x];i;i=nx[i]){
            int v=to[i];if(v==fa)continue;
            f[v][0]=x;
            dfs(v,x);
        }
        return ;
    }

    int lca(int x,int y){
        if(dep[x]<dep[y])swap(x,y);
        for(int i=20;i>=0;i--){
            if(dep[f[x][i]]>=dep[y])x=f[x][i];
        }
        if(x==y)return x;
        for(int i=20;i>=0;i--){
            if(f[x][i]!=f[y][i]){
                x=f[x][i];
                y=f[y][i];
            }
        }
        return f[x][0];
    }

    int getdis(int x,int y){
        return dep[x]+dep[y]-2*dep[lca(x,y)];
    }

}T;

struct Vtree{
    int tot,head[maxn],nx[maxn],to[maxn];
    void add(int x,int y){
        to[++tot]=y;nx[tot]=head[x];head[x]=tot;
        to[++tot]=x;nx[tot]=head[y];head[y]=tot;
    }

    int h[maxn],k;    //储存关键点
    int st[maxn],top;
    set<int>s;  //关键点的集合
    void build(){
        sort(h+1,h+k+1,[](int i,int j){
            return T.dfn[i]<T.dfn[j];
        });
        st[top=1]=1;
        for(int i=1;i<=k;i++){
            if(h[i]!=1){
                int l=T.lca(h[i],st[top]);
                if(l!=st[top]){
                    while(T.dfn[l]<T.dfn[st[top-1]]){
                        add(st[top],st[top-1]);
                        top--;
                    }
                    if(T.dfn[l]>T.dfn[st[top-1]]){
                        add(l,st[top]);
                        st[top]=l;

                    }
                    else add(l,st[top--]);
                }
                st[++top]=h[i];
            }
        }
        for(int i=1;i<top;i++)add(st[i],st[i+1]);
        return ;
    }

    void init(){
        tot=0;ans=0;
        s.clear();
    }
    int ans;
    int dfs(int u,int fa){
        int cnt=0;
        for(int i=head[u];i;i=nx[i]){
            int v=to[i];if(v==fa)continue;
            cnt+=dfs(v,u);
        }
        head[u]=0;
        if(!s.count(u)){
            if(cnt>1)ans++,cnt=0;
            return cnt;
        }
        else {
            ans+=cnt;
            return 1;
        }
    }  

    void solve(){
        k=read();init();
        for(int i=1;i<=k;i++){
            h[i]=read();
            s.insert(h[i]);
        }
        for(int i=1;i<=k;i++){
            if(s.count(T.f[h[i]][0])){puts("-1");return ;}
        }
        build();
        dfs(1,0);
        printf("%d\n",ans);
        return ;
    }
}V;

int n,m;
int main(){
    n=read();
    for(int i=1;i<n;i++){
        int x=read(),y=read();
        T.add(x,y);T.add(y,x);
    }
    T.dfs(1,0);
    m=read();
    while(m--)V.solve();
    return 0;
}

posted @ 2022-07-26 16:45  I_N_V  阅读(32)  评论(0编辑  收藏  举报