zoukankan      html  css  js  c++  java
  • Luogu P4299 首都

    LCT维护重心

    考虑合并两个树找重心

    LCT维护子树SZ

    法一:

    暴力插入,启发式合并。每次插入一个考虑是否要把重心进行移动。条件是这条边的两边的子树sz哪个更大。相同则取编号小的。

    O(Nlog^2N)不够优秀

    法二:

    找重心太暴力

    两个树新的重心一定在重心相连的路径上。否则一定会有一个子树sz>sum/2

    考虑对路径进行二分

    怎么二分?

    splay本身具有优秀的logn高度的性质,左右儿子则分别是链的向上和向下部分

    决策重心与否唯一要考虑的是路径两大块是否sz<=sum/2都成立(其他小子树之前不影响,现在一定也不影响)

    由于会排除一些区间,所以lsum=0,rsum=0来处理两大块已经排除过去的sz

    把树立起来

    从x开始往下找。

    只要考虑当前是否t[ls].sz+lsum<=sum/2&&t[rs].sz+rsum<=sum/2,是的话,就是一个重心。奇数点直接return,偶数点还要继续找(反正最多logn次)

     找t[].sz+[]sum较大的一个。并且较小的一半[^1]sum+=t[x].si+1+t[^1].sz(注意t[x].si,小的子树别忽略)

    找到了叶子,一定找到了答案,返回即可。

    可以外面再用并查集维护所在树的重心,方便快速查找。

    注意:

    1.t[x].si,小的子树别忽略

    2.link(x,y)的时候,更新t[x].si,pushup(x)虚子树的sz加进来

    3.upda的时候pushdown(x)(我也不知道为什么??感觉之间access已经pushdown完了??)

    (upda:2019.3.13:显然不会pushdown完的。。。)

    #include<bits/stdc++.h>
    #define reg register int
    #define il inline
    #define ls t[x].ch[0]
    #define rs t[x].ch[1]
    #define numb (ch^'0')
    using namespace std;
    typedef long long ll;
    il void rd(int &x){
        char ch;x=0;bool fl=false;
        while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);
        for(x=numb;isdigit(ch=getchar());x=x*10+numb);
        (fl==true)&&(x=-x);
    }
    namespace Miracle{
    const int N=1e5+5;
    const int inf=0x3f3f3f3f;
    int n,m;
    struct node{
        int fa,ch[2];
        int s,si;
        int r;
    }t[N];
    int fa[N],rt[N],sz[N];
    int xo;
    int fin(int x){
        return fa[x]==x?x:fa[x]=fin(fa[x]);
    }
    bool nrt(int x){
        return (t[t[x].fa].ch[0]==x||t[t[x].fa].ch[1]==x);
    }
    void rev(int x){
        swap(ls,rs);
        t[x].r^=1;
    }
    void pushdown(int x){
        if(t[x].r){
            rev(ls);rev(rs);
            t[x].r=0;
        }
    }
    void pushup(int x){
        if(x) t[x].s=t[ls].s+t[rs].s+t[x].si+1;
    }
    void rotate(int x){
        int y=t[x].fa,d=t[y].ch[1]==x;
        t[t[y].ch[d]=t[x].ch[!d]].fa=y;
        if(nrt(y)) t[t[x].fa=t[y].fa].ch[t[t[y].fa].ch[1]==y]=x;
        else t[x].fa=t[y].fa;
        t[t[x].ch[!d]=y].fa=x;
        pushup(y);
    }
    int sta[N];
    void splay(int x){
        int y=x,z=0;
        sta[++z]=y;
        while(nrt(y)) y=t[y].fa,sta[++z]=y;
        while(z) pushdown(sta[z--]);
        
        while(nrt(x)){
            y=t[x].fa,z=t[y].fa;
            if(nrt(y)){
                rotate(((t[y].ch[0]==x)==(t[z].ch[0]==y)?y:x));
            }
            rotate(x);
        }
        pushup(x);
    }
    void access(int x){
        for(reg y=0;x;y=x,x=t[x].fa){
            splay(x);
            t[x].si+=t[t[x].ch[1]].s;
            t[x].si-=t[y].s;
            t[x].ch[1]=y;
            pushup(x);
        }
    }
    void makert(int x){
        access(x);splay(x);rev(x);
    }
    void split(int x,int y){
        makert(x);access(y);splay(y);
    }
    void link(int x,int y){
        split(x,y);
        t[x].fa=y;
        t[y].si+=t[x].s;pushup(y);
    }
    int upda(int x){//and find zhong
        int sum=t[x].s;
        int lsum=0,rsum=0;
        int id=inf;
        while(x){
            pushdown(x);
            if(t[rs].s+rsum<=sum/2&&t[ls].s+lsum<=sum/2){
                id=min(id,x);
                if(sum&1) break;
            }
            if(t[rs].s+rsum<t[ls].s+lsum){
                rsum+=t[x].si+t[rs].s+1;x=ls;
            }else{
                lsum+=t[x].si+t[ls].s+1;x=rs;
            }
        }
        splay(id);
        return id;
        //don't forget to upda fa[x],sz[x],rt[x]
        //don't forget to upda XOR
    }
    int main(){
        rd(n);rd(m);
        for(reg i=1;i<=n;++i) fa[i]=i,rt[i]=i,xo^=i,t[i].s=1,sz[i]=1;
        char s[10];
        int x,y;
        while(m--){
            scanf("%s",s+1);
            if(s[1]=='X'){
                printf("%d
    ",xo);
            }else if(s[1]=='A'){
                rd(x);rd(y);
                link(x,y);
                split(x=fin(x),y=fin(y));
                int z=upda(y);
                xo^=y^x^z;
                fa[x]=fa[y]=fa[z]=z;
            }else{
                rd(x);
                x=fin(x);
                printf("%d
    ",x);
            }
        }
        return 0;
    }
    
    }
    signed main(){
        Miracle::main();
        return 0;
    }
    
    /*
       Author: *Miracle*
       Date: 2018/12/27 15:32:41
    */

     总结:

    splay上二分找重心的思路值得注意!

    二分不光是mid=(l+r)>>1

    线段树,平衡树logn树高的结构,也许都可以支持二分!(二分本质也是一棵树的一条链,不同之处是“线段树”那种二分树,本题是“平衡树”的那种)

  • 相关阅读:
    【转载】Linux 内存管理机制
    【学习笔记】cache/buffer
    【错误记录】PowerShell 超级无语的语法错误(令人怀疑人生)
    【Ansible 文档】【译文】模版(Jinja2)
    【Ansible 文档】【译文】Playbooks 变量
    【Ansible 文档】提示、推荐、注意事项
    【Ansible 文档】【译文】网络支持
    银行卡算法规则
    网站优化:浏览器缓存控制简介及配置策略
    学习一份百度的项目目录结构规范
  • 原文地址:https://www.cnblogs.com/Miracevin/p/10186872.html
Copyright © 2011-2022 走看看