• [ZJOI2016]大森林


    Description:

    小Y家里有一个大森林,里面有n棵树,编号从1到n

    0 l r 表示将第 l 棵树到第 r 棵树的生长节点下面长出一个子节点,子节点的标号为上一个 0 号操作叶子标号加 1(例如,第一个 0 号操作产生的子节点标号为 2), l 到 r 之间的树长出的节点标号都相同。保证 1<=l<=r<=n 。

    1 l r x 表示将第 l 棵树到第 r 棵树的生长节点改到标号为 x 的节点。对于 i (l<=i<=r)这棵树,如果标号 x的点不在其中,那么这个操作对该树不产生影响。保证 1<=l<=r<=n , x 不超过当前所有树中节点最大的标号。

    2 x u v 询问第 x 棵树中节点 u 到节点 v 点的距离,也就是在第 x 棵树中从节点 u 和节点 v 的最短路上边的数量。保证1<=x<=n,这棵树中节点 u 和节点 v 存在。

    Hint:

    $ N<=105,M<=2*105 $

    Solution:

    一开始想到线段树套LCT,然而标记无法下传
    考虑朴素做法,每个节点用LCT维护一片森林,空间显然无法承受
    这时我们需要转换思路
    因为题目没有强制在线,所以我们可以只维护一颗”树“,然后按顺序处理1、2操作
    每次1操作直接把该生长节点的子树”嫁接“到另一个生长节点
    为了保证复杂度,我们需要在生长节点处开一个虚点,这样就不需要多点换父亲了
    2操作直接用求个LCA就行了

    #include<bits/stdc++.h>
    using namespace std;
    const int mxn=3e5+5;
    struct Q {
        int pos,id,x,y,qr;
    }q[mxn];
    int n,m,p,s,tot,cnt,sum;
    int t[mxn],lp[mxn],rp[mxn],bl[mxn],fa[mxn],ch[mxn][2],ans[mxn],val[mxn];
    
    int cmp(Q x,Q y) {
        return x.pos==y.pos?x.id<y.id:x.pos<y.pos;
    }
    
    namespace lct {
        int isnotrt(int x) {
            return ch[fa[x]][0]==x||ch[fa[x]][1]==x;
        }
        void push_up(int x) {
            t[x]=t[ch[x][0]]+t[ch[x][1]]+val[x];
        }
        void rotate(int x) {
            int y=fa[x],z=fa[y],tp=ch[y][1]==x;
            if(isnotrt(y)) ch[z][ch[z][1]==y]=x; fa[x]=z;
            ch[y][tp]=ch[x][tp^1]; fa[ch[x][tp^1]]=y;
            ch[x][tp^1]=y; fa[y]=x;
            push_up(y),push_up(x);
        }
        void splay(int x) {
            while(isnotrt(x)) {
                int y=fa[x],z=fa[y];
                if(isnotrt(y)) 
                    (ch[y][1]==x)^(ch[z][1]==y)?rotate(x):rotate(y);
                rotate(x);	
            }
        }
        int access(int x) {
            int y;
            for(y=0;x;x=fa[y=x])  
                splay(x),ch[x][1]=y,push_up(x);
            return y;	
        }
        void link(int x,int y) {
            splay(x); fa[x]=y;
        }
        void cut(int x) {
            access(x); splay(x);  
            ch[x][0]=fa[ch[x][0]]=0; 
            push_up(x);
        }
    }
    using namespace lct;
    
    int main()
    {
        scanf("%d%d",&n,&m); int res,lca,opt,l,r,x,y; 
        p=tot=val[1]=t[1]=lp[1]=bl[1]=1; rp[1]=n; int now=2; link(++p,1);
        for(int i=1;i<=m;++i) {
            scanf("%d",&opt);
            if(opt==0) {
                scanf("%d%d",&l,&r);
                bl[++tot]=++p; link(p,now); //每次把新节点直接长在最近更新的生成节点,考虑这样为什么不会错,因为实点之间的虚点不会影响答案
                lp[tot]=l,rp[tot]=r; 
                val[p]=t[p]=1;
            }
            else if(opt==1) {
                scanf("%d%d%d",&l,&r,&x); 
                l=max(l,lp[x]); r=min(r,rp[x]); 
                if(l>r) continue;//去掉无用的区间
                link(++p,now);
                q[++s]=(Q){l,i,p,bl[x],0};
                q[++s]=(Q){r+1,i,p,now,0};
                now=p;
            }
            else scanf("%d%d%d",&l,&x,&y),q[++s]=(Q){l,i,bl[x],bl[y],++sum};
        }
        sort(q+1,q+s+1,cmp); 
        for(int i=1;i<=s;++i) {
            if(q[i].qr>0) {
                access(q[i].x); splay(q[i].x); res=t[q[i].x]; //因为这题规定根为1,所以我们不能makert
                lca=access(q[i].y); splay(q[i].y); res+=t[q[i].y];
                access(lca); ans[q[i].ss]=res-t[lca]*2; 
            }
            else cut(q[i].x),link(q[i].x,q[i].y);
        }
        for(int i=1;i<=sum;++i) printf("%d
    ",ans[i]);
        return 0;
    }
    
  • 相关阅读:
    浅复制(Shallow Copy)与深复制(Deep Copy)
    使Web API支持二级实体操作,兼对RESTFul风格API设计的疑惑。
    ActionResult
    如何在github上fork一个项目来贡献代码以及同步原作者的修改
    实用技巧:Google 搜索打不开的解决方法
    C# 汉字转拼音
    Windows Server AppFabric的安装、配置与问题排除
    小工具,大智慧(一)                     ——notepad++
    通用软件注册功能之建立有效的软件保护机制
    Javascript MVVM模式前端框架—Knockout 2.1.0系列:目录
  • 原文地址:https://www.cnblogs.com/list1/p/10421740.html
Copyright © 2020-2023  润新知