• 【BZOJ】3572: [Hnoi2014]世界树 虚树+倍增


    【题意】给定n个点的树,m次询问,每次给定ki个特殊点,一个点会被最近的特殊点控制,询问每个特殊点控制多少点。n,m,Σki<=300000。

    【算法】虚树+倍增

    【题解】★参考:thy_asdf

    对询问建立虚树,然后DFS统计虚树上每个点被哪个点控制,记为belong[x]。

    统计的方法是从下往上DFS得到x来自x子树的控制点,再从上往下DFS得到x来自非x子树的控制点。

    令D(x,y)表示点x和点y之间的路径长度,比较x的不同控制点y的方式是取D(x,y)的最小值(相同时比较编号)。

    依此考虑虚树上的每一条边(x,y),记z为同时是x的儿子和y的祖先的点,令f[x]表示点x的答案。

    如果belong[x]=belong[y],那么f[belong[x]]+=size[z]-size[y]

    否则,从y倍增找到分界点mid,满足mid及以下由y控制,mid以上由x控制。

    倍增过程中,记A=D(belong[x],mid),B=D(belong[x],mid),当满足A>B或A=B&&belong[x]>belong[y]时进行倍增。

    找到分界点mid后,f[belong[x]]+=size[x]-size[m],f[belong[y]+=size[m]-size[y]。

    最后要处理没有出现在虚树路径上的点(包括虚树节点),这些点没有在上述过程中被统计。

    记rem[x]表示子树x中未被统计的节点数,初始rem[x]=size[x],特别的rem[a[1]]=n(a[1]是虚树最高节点,n是原树总点数,这样是为了保存a[1]往上延伸的节点)

    每次处理完边(x,y),rem[x]-=size[z]。

    全部做完后,对虚树的每个点f[belong[x]]+=rem[x]。

    #include<cstdio>
    #include<cctype>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    int read(){
        int s=0,t=1;char c;
        while(!isdigit(c=getchar()))if(c=='-')t=-1;
        do{s=s*10+c-'0';}while(isdigit(c=getchar()));
        return s*t;
    }
    const int maxn=300010;
    int n,N,size[maxn],in[maxn],ou[maxn],f[maxn],a[maxn*2],b[maxn];
    int deep[maxn],tot,first[maxn],belong[maxn],st[maxn];
    int rem[maxn];
    bool v[maxn];
    namespace cyc{
        int first[maxn],tot,dfsnum=0,f[maxn][30];
        struct edge{int v,from;}e[maxn*2];
        void insert(int u,int v){tot++;e[tot].v=v;e[tot].from=first[u];first[u]=tot;}
        void dfs(int x,int fa){
            in[x]=++dfsnum;size[x]=1;
            for(int j=1;(1<<j)<=deep[x];j++)f[x][j]=f[f[x][j-1]][j-1];
            for(int i=first[x];i;i=e[i].from)if(e[i].v!=fa){
                deep[e[i].v]=deep[x]+1;
                f[e[i].v][0]=x;
                dfs(e[i].v,x);
                size[x]+=size[e[i].v];
            }
            ou[x]=dfsnum;
        }
        int lca(int x,int y){
            if(deep[x]<deep[y])swap(x,y);
            int d=deep[x]-deep[y];
            for(int j=0;j<=20;j++)if(d&(1<<j))x=f[x][j];
            if(x==y)return x;
            for(int j=20;j>=0;j--)if((1<<j)<=deep[x]&&f[x][j]!=f[y][j]){
                x=f[x][j];y=f[y][j];
            }
            return f[x][0];
        }
        void build(){
            n=read();
            for(int i=1;i<n;i++){
                int u=read(),v=read();
                insert(u,v);insert(v,u);
            }
            dfs(1,-1);
        }
    }
    int D(int x,int y){if(!y)return 0x3f3f3f3f;else return deep[x]+deep[y]-2*deep[cyc::lca(x,y)];}
    bool cmp(int a,int b){return in[a]<in[b];}
    bool check(int x,int y){return in[x]<=in[y]&&ou[y]<=ou[x];}
    struct edge{int u,v,from;}e[maxn*2];
    void insert(int u,int v){tot++;e[tot].u=u;e[tot].v=v;e[tot].from=first[u];first[u]=tot;}
    void dfs1(int x){
        if(v[x])belong[x]=x;
        for(int i=first[x];i;i=e[i].from){
            dfs1(e[i].v);
            int A=D(x,belong[x]),B=D(x,belong[e[i].v]);
            if(B<A||(B==A&&belong[e[i].v]<belong[x]))belong[x]=belong[e[i].v];
        }
    }
    void dfs2(int x){
        for(int i=first[x];i;i=e[i].from){
            int A=D(e[i].v,belong[x]),B=D(e[i].v,belong[e[i].v]);
            if(A<B||(A==B&&belong[x]<belong[e[i].v]))belong[e[i].v]=belong[x];
            dfs2(e[i].v);
        }
    }    
    void solve(int x,int y){
        int z=y;
        for(int i=20;i>=0;i--)if((1<<i)<=deep[z]&&deep[cyc::f[z][i]]>=deep[x]+1)z=cyc::f[z][i];
        rem[x]-=size[z];
        if(belong[x]==belong[y]){f[belong[x]]+=size[z]-size[y];return;}//
        int m=y;
        for(int i=20;i>=0;i--)if((1<<i)<=deep[m]){
            if(deep[cyc::f[m][i]]<deep[x])continue;
            int A=D(cyc::f[m][i],belong[x]),B=D(cyc::f[m][i],belong[y]);
            if(B<A||(A==B&&belong[x]>belong[y]))m=cyc::f[m][i];//
        }
        f[belong[x]]+=size[z]-size[m];
        f[belong[y]]+=size[m]-size[y];
    }
    void work(){
        int last=read();N=last;
        for(int i=1;i<=N;i++)a[i]=read(),f[b[i]=a[i]]=0,v[a[i]]=1;
        sort(a+1,a+N+1,cmp);
        for(int i=1;i<last;i++)a[++N]=cyc::lca(a[i],a[i+1]);
        sort(a+1,a+N+1,cmp);
        N=unique(a+1,a+N+1)-a-1;
        tot=0;
        for(int i=1;i<=N;i++)first[a[i]]=0,belong[a[i]]=0,rem[a[i]]=size[a[i]];
        rem[a[1]]=n;
        int top=0;
        for(int i=1;i<=N;i++){
            while(top&&!check(st[top],a[i]))top--;
            if(top)insert(st[top],a[i]);
            st[++top]=a[i];
        }
        dfs1(a[1]);dfs2(a[1]);
        for(int i=1;i<=tot;i++)solve(e[i].u,e[i].v);
        for(int i=1;i<=N;i++)f[belong[a[i]]]+=rem[a[i]];
        for(int i=1;i<=last;i++)printf("%d ",f[b[i]]),v[b[i]]=0;
    }
    int main(){
        cyc::build();
        int m=read();
        while(m--)work();
        return 0;
    }
    View Code

     (倍增求LCA)

    注意,由于多次调用LCA,所以用倍增LCA算法有很大的常数,甚至在LOJ会导致TLE。

    但是倍增求LCA会使过程更简洁。

    下面这份代码是树链剖分求LCA,代码可读性不高。

    #include<cstdio>
    #include<cctype>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    int read(){
        int s=0,t=1;char c;
        while(!isdigit(c=getchar()))if(c=='-')t=-1;
        do{s=s*10+c-'0';}while(isdigit(c=getchar()));
        return s*t;
    }
    const int maxn=300010;
    int n,N,size[maxn],in[maxn],ou[maxn],f[maxn],a[maxn*2],b[maxn];
    int deep[maxn],tot,first[maxn],belong[maxn],st[maxn];
    int rem[maxn];
    bool v[maxn];
    namespace cyc{
        int first[maxn],tot,dfsnum=0,f[maxn][30];
        struct edge{int v,from;}e[maxn*2];
        void insert(int u,int v){tot++;e[tot].v=v;e[tot].from=first[u];first[u]=tot;}
        void dfs(int x,int fa){
            in[x]=++dfsnum;size[x]=1;
            for(int j=1;(1<<j)<=deep[x];j++)f[x][j]=f[f[x][j-1]][j-1];
            for(int i=first[x];i;i=e[i].from)if(e[i].v!=fa){
                deep[e[i].v]=deep[x]+1;
                f[e[i].v][0]=x;
                dfs(e[i].v,x);
                size[x]+=size[e[i].v];
            }
            ou[x]=dfsnum;
        }
        int lca(int x,int y){
            if(deep[x]<deep[y])swap(x,y);
            int d=deep[x]-deep[y];
            for(int j=0;j<=20;j++)if(d&(1<<j))x=f[x][j];
            if(x==y)return x;
            for(int j=20;j>=0;j--)if((1<<j)<=deep[x]&&f[x][j]!=f[y][j]){
                x=f[x][j];y=f[y][j];
            }
            return f[x][0];
        }
        void build(){
            n=read();
            for(int i=1;i<n;i++){
                int u=read(),v=read();
                insert(u,v);insert(v,u);
            }
            dfs(1,-1);
        }
    }
    int D(int x,int y){if(!y)return 0x3f3f3f3f;else return deep[x]+deep[y]-2*deep[cyc::lca(x,y)];}
    bool cmp(int a,int b){return in[a]<in[b];}
    bool check(int x,int y){return in[x]<=in[y]&&ou[y]<=ou[x];}
    struct edge{int u,v,from;}e[maxn*2];
    void insert(int u,int v){tot++;e[tot].u=u;e[tot].v=v;e[tot].from=first[u];first[u]=tot;}
    void dfs1(int x){
        if(v[x])belong[x]=x;
        for(int i=first[x];i;i=e[i].from){
            dfs1(e[i].v);
            int A=D(x,belong[x]),B=D(x,belong[e[i].v]);
            if(B<A||(B==A&&belong[e[i].v]<belong[x]))belong[x]=belong[e[i].v];
        }
    }
    void dfs2(int x){
        for(int i=first[x];i;i=e[i].from){
            int A=D(e[i].v,belong[x]),B=D(e[i].v,belong[e[i].v]);
            if(A<B||(A==B&&belong[x]<belong[e[i].v]))belong[e[i].v]=belong[x];
            dfs2(e[i].v);
        }
    }    
    void solve(int x,int y){
        int z=y;
        for(int i=20;i>=0;i--)if((1<<i)<=deep[z]&&deep[cyc::f[z][i]]>=deep[x]+1)z=cyc::f[z][i];
        rem[x]-=size[z];
        if(belong[x]==belong[y]){f[belong[x]]+=size[z]-size[y];return;}//
        int m=y;
        for(int i=20;i>=0;i--)if((1<<i)<=deep[m]){
            if(deep[cyc::f[m][i]]<deep[x])continue;
            int A=D(cyc::f[m][i],belong[x]),B=D(cyc::f[m][i],belong[y]);
            if(B<A||(A==B&&belong[x]>belong[y]))m=cyc::f[m][i];//
        }
        f[belong[x]]+=size[z]-size[m];
        f[belong[y]]+=size[m]-size[y];
    }
    void work(){
        int last=read();N=last;
        for(int i=1;i<=N;i++)a[i]=read(),f[b[i]=a[i]]=0,v[a[i]]=1;
        sort(a+1,a+N+1,cmp);
        for(int i=1;i<last;i++)a[++N]=cyc::lca(a[i],a[i+1]);
        sort(a+1,a+N+1,cmp);
        N=unique(a+1,a+N+1)-a-1;
        tot=0;
        for(int i=1;i<=N;i++)first[a[i]]=0,belong[a[i]]=0,rem[a[i]]=size[a[i]];
        rem[a[1]]=n;
        int top=0;
        for(int i=1;i<=N;i++){
            while(top&&!check(st[top],a[i]))top--;
            if(top)insert(st[top],a[i]);
            st[++top]=a[i];
        }
        dfs1(a[1]);dfs2(a[1]);
        for(int i=1;i<=tot;i++)solve(e[i].u,e[i].v);
        for(int i=1;i<=N;i++)f[belong[a[i]]]+=rem[a[i]];
        for(int i=1;i<=last;i++)printf("%d ",f[b[i]]),v[b[i]]=0;
    }
    int main(){
        cyc::build();
        int m=read();
        while(m--)work();
        return 0;
    }
    View Code

    upd:复习一下。

    【建虚树】

    第一步、所有关键点按DFS序排序,加入LCA后排序去重。

    第二步、按顺序用栈维护构造出虚树。

    第三步、虚树DP后清零。

    【此题的虚树DP】

    第一步、处理虚树上点与点。

    第二步、处理虚树边上点的子树。

    第三步、处理虚树点的 [ 不在虚树上的孩子 ] 子树。(通过减虚树孩子的sz得到)

  • 相关阅读:
    汇编指令速查
    七种寻址方式(直接寻址方式)
    七种寻址方式(立即寻址、寄存器寻址)
    七种寻址方式(寄存器间接寻址方式)
    Indy IdHttp get Gb2312乱码的解决
    七种寻址方式(相对基址加变址寻址方式)
    七种寻址方式(寄存器相对寻址方式)
    【Note】2012.10.3
    算法04不重复打印排序数组中相加和为给定值的所有三元组
    算法03不重复打印排序数组中相加和为给定值的所有二元组
  • 原文地址:https://www.cnblogs.com/onioncyc/p/8480787.html
Copyright © 2020-2023  润新知