zoukankan      html  css  js  c++  java
  • bzoj 4817

    LCT好题

    首先我们考虑实际询问的是什么:

    从LCT的角度考虑,如果我们认为一开始树上每一条边都是虚边,把一次涂色看作一次access操作,那么询问的实际就是两个节点间的虚边数量+1和子树中的最大虚边数量!

    这种问题显然树上容斥,如果设$dis_{i}$表示$i$到根节点需要经过多少虚边,那么答案显然是$dis_{x}+dis_{y}-2dis_{LCA(x,y)}+1$

    那么我们实际只是在维护这个$dis_{i}$

    怎么维护?

    我们注意到,虚实边的转化对应着access操作,因此我们肯定考虑access中进行维护

    注意到在每次access的时候,实际对应着三步操作:

    第一步:将节点旋转到根

    第二步:将节点与右子树之间的边由实边断为虚边

    第三步:将节点与原先节点之间的边由虚边变为实边

    那么实边变为虚边对应着虚边数量增加,虚边变为实边对应着虚边数量减少

    由于这棵树的形态不变,我们考虑直接用线段树维护dfs序,这样就支持了单点查询$dis$和查询子树中最大的$dis$

    在修改的时候,考虑到每次access操作的过程,每一次虚实边的变化实际影响的是一棵子树

    而这棵子树的根就是在splay上右子树中最靠左的那个点!

    因此我们找到这个根,更新即可,区间修改,单点查询+区间查询即可

    贴代码:

    #include <cstdio>
    #include <cmath>
    #include <cstring>
    #include <cstdlib>
    #include <iostream>
    #include <algorithm>
    #include <queue>
    #include <stack>
    #define rt1 rt<<1
    #define rt2 (rt<<1)|1
    using namespace std;
    struct Seg_tree
    {
        int lazy,maxv;
    }tree[400005];
    struct Edge
    {
        int nxt,to;
    }edge[200005];
    int head[100005];
    int ch[100005][2];
    int f[100005];
    int fa[100005][25];
    int dep[100005];
    int fl[100005];
    int inr[100005],our[100005],onum[100005];
    int cnt=1,tot;
    int n,m;
    void add(int l,int r)
    {
        edge[cnt].nxt=head[l];
        edge[cnt].to=r;
        head[l]=cnt++;
    }
    void dfs(int x,int fx)
    {
        dep[x]=dep[fx]+1,fa[x][0]=f[x]=fx,inr[x]=++tot,onum[tot]=x;
        for(int i=1;i<=20;i++)fa[x][i]=fa[fa[x][i-1]][i-1];
        for(int i=head[x];i;i=edge[i].nxt)
        {
            int to=edge[i].to;
            if(to==fx)continue;
            dfs(to,x);
        }
        our[x]=tot;
    }
    int LCA(int x,int y)
    {
        if(dep[x]>dep[y])swap(x,y);
        for(int i=20;i>=0;i--)if(dep[fa[y][i]]>=dep[x])y=fa[y][i];
        if(x==y)return x;
        int ret;
        for(int i=20;i>=0;i--)
        {
            if(fa[x][i]!=fa[y][i])x=fa[x][i],y=fa[y][i];
            else ret=fa[x][i];
        }
        return ret;
    }
    void buildtree(int rt,int l,int r)
    {
        if(l==r){tree[rt].maxv=dep[onum[l]];return;}
        int mid=(l+r)>>1;
        buildtree(rt1,l,mid),buildtree(rt2,mid+1,r);
        tree[rt].maxv=max(tree[rt1].maxv,tree[rt2].maxv);
    }
    void down(int rt)
    {
        tree[rt1].lazy+=tree[rt].lazy,tree[rt1].maxv+=tree[rt].lazy;
        tree[rt2].lazy+=tree[rt].lazy,tree[rt2].maxv+=tree[rt].lazy;
        tree[rt].lazy=0;
    }
    void update(int rt,int l,int r,int lq,int rq,int v)
    {
        if(l>=lq&&r<=rq){tree[rt].lazy+=v;tree[rt].maxv+=v;return;}
        int mid=(l+r)>>1;
        if(tree[rt].lazy)down(rt);
        if(lq<=mid)update(rt1,l,mid,lq,rq,v);
        if(rq>mid)update(rt2,mid+1,r,lq,rq,v);
        tree[rt].maxv=max(tree[rt1].maxv,tree[rt2].maxv);
    }
    int query(int rt,int l,int r,int lq,int rq)
    {
        if(l>=lq&&r<=rq)return tree[rt].maxv;
        int mid=(l+r)>>1;
        if(tree[rt].lazy)down(rt);
        int ret=0;
        if(lq<=mid)ret=max(ret,query(rt1,l,mid,lq,rq));
        if(rq>mid)ret=max(ret,query(rt2,mid+1,r,lq,rq));
        return ret;
    }
    bool be_root(int x)
    {
        if(ch[f[x]][0]==x||ch[f[x]][1]==x)return 0;
        return 1;
    }
    void pushdown(int x)
    {
        if(fl[x])
        {
            swap(ch[ch[x][0]][0],ch[ch[x][0]][1]);
            swap(ch[ch[x][1]][0],ch[ch[x][1]][1]);
            fl[ch[x][0]]^=1,fl[ch[x][1]]^=1;
            fl[x]=0;
        }
    }
    void repush(int x)
    {
        if(!be_root(x))repush(f[x]);
        pushdown(x);
    }
    void rotate(int x)
    {
        int y=f[x],z=f[y],k=(ch[y][1]==x);
        if(!be_root(y))ch[z][ch[z][1]==y]=x;
        f[x]=z;
        ch[y][k]=ch[x][!k],f[ch[x][!k]]=y;
        ch[x][!k]=y,f[y]=x;
    }
    void splay(int x)
    {
        repush(x);
        while(!be_root(x))
        {
            int y=f[x],z=f[y];
            if(!be_root(y))
            {
                if((ch[y][1]==x)^(ch[z][1]==y))rotate(x);
                else rotate(y);
            }
            rotate(x);
        }
    }
    int get_root(int x)
    {
        while(ch[x][0])x=ch[x][0];
        return x;
    }
    void access(int x)
    {
        int y=0;
        while(x)
        {
            splay(x);
            if(ch[x][1])
            {
                int rt=get_root(ch[x][1]);
                update(1,1,n,inr[rt],our[rt],1);
            }
            ch[x][1]=y;
            if(y)
            {
                int rt=get_root(y);
                update(1,1,n,inr[rt],our[rt],-1);
            }
            y=x,x=f[x];
        }
    }
    inline int read()
    {
        int f=1,x=0;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    int main()
    {
        n=read(),m=read();
        for(int i=1;i<n;i++)
        {
            int x=read(),y=read();
            add(x,y),add(y,x);
        }
        dfs(1,0),buildtree(1,1,n);
        while(m--)
        {
            int typ=read();
            if(typ==1)
            {
                int x=read();
                access(x);
            }else if(typ==2)
            {
                int x=read(),y=read(),fa=LCA(x,y);
                int s1=query(1,1,n,inr[x],inr[x]),s2=query(1,1,n,inr[y],inr[y]),s3=query(1,1,n,inr[fa],inr[fa]);
                printf("%d
    ",s1+s2-2*s3+1);
            }else
            {
                int x=read();
                printf("%d
    ",query(1,1,n,inr[x],our[x]));
            }
        }
        return 0;
    }
  • 相关阅读:
    Array中使用异步函数遍历元素,Array循环同步执行
    vscode设置快捷键"h"快速生成html模板
    IOS(苹果手机)使用video播放HLS流,实现在内部播放及全屏播放(即非全屏和全屏播放)。
    FTP服务器与客户端的安装与配置
    移动端页面顶部滑动实现菜单的弹出与隐藏
    JS十大经典排序排序算法
    【bug】table重新加载数据,页面滚动条下沉到底部,记录scrollTop后将其恢复scrollTop出现闪烁
    寄生组合式继承
    扁平对象,转为树形对象
    使用CSS禁止textarea调整大小功能的方法
  • 原文地址:https://www.cnblogs.com/zhangleo/p/11163185.html
Copyright © 2011-2022 走看看