• [bzoj2733][HNOI2012]永无乡


    来自FallDream的博客,未经允许,请勿转载,谢谢。


    永无乡包含 n 座岛,编号从 1 到 n,每座岛都有自己的独一无二的重要度,按照重要度可 以将这 n 座岛排名,名次用 1 到 n 来表示。某些岛之间由巨大的桥连接,通过桥可以从一个岛 到达另一个岛。如果从岛 a 出发经过若干座(含 0 座)桥可以到达岛 b,则称岛 a 和岛 b 是连 通的。现在有两种操作:B x y 表示在岛 x 与岛 y 之间修建一座新桥。Q x k 表示询问当前与岛 x连通的所有岛中第 k 重要的是哪座岛,即所有与岛 x 连通的岛中重要度排名第 k 小的岛是哪 座,请你输出那个岛的编号。 n≤100000,m≤n,q≤300000 

    这道题很容易看出来是平衡树+启发式合并  

    就是每一个联通块都维护一棵平衡树,然后建桥的时候如果两个点不在同一颗树,那么就把它们所在的平衡树合并。合并的时候选择把size较小的平衡树的点全部拿出来,插入到另一棵里面,可以证明这样的复杂度是最坏$O(nlog^{2}n)$的。

    #include<iostream>
    #include<cstdio>
    #define MN 100000
    using namespace std;
    inline int read()
    {
        int x = 0 , f = 1; 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 fa[MN+5],bel[MN+5],size[MN+5],c[MN+5][2],n,m,rt[MN+5],top,q[MN+5],s[MN+5];
    inline void update(int x){size[x]=size[c[x][0]]+size[c[x][1]]+1;}
    
    void rotate(int x,int&k)
    {
        int y=fa[x],z=fa[y],l=c[y][1]==x,r=l^1;
        if(y==k) k=x; else c[z][c[z][1]==y]=x;
        fa[x]=z;fa[y]=x;fa[c[x][r]]=y;
        c[y][l]=c[x][r];c[x][r]=y;
        update(y);update(x);
    }
    
    void splay(int x,int&k)
    {
        for(;x!=k;rotate(x,k))
            if(fa[x]!=k) rotate((c[fa[fa[x]]][1]==fa[x]^c[fa[x]][1]==x)?x:fa[x],k);
    }
    
    void ins(int&x,int k,int last)
    {
        if(!x){x=k;fa[x]=last;size[x]=1;c[x][0]=c[x][1]=0;return;}
        ins(c[x][s[k]>s[x]],k,x);++size[x];
    }
    
    void dfs(int x)
    {
        if(c[x][0]) dfs(c[x][0]);
        q[++top]=x;
        if(c[x][1]) dfs(c[x][1]);
    }
    
    void Merge(int x,int y)
    {
        splay(x,rt[bel[x]]);splay(y,rt[bel[y]]);
        if(size[x]>size[y]) swap(x,y);
        top=0;dfs(x);
        for(int i=1;i<=top;i++) bel[q[i]]=bel[y],ins(rt[bel[y]],q[i],0),splay(q[i],rt[bel[y]]);
    }
    
    int find(int x,int rk)
    {
        int sz=size[c[x][0]]+1;
        if(sz==rk) return x;
        if(sz>rk) return find(c[x][0],rk);
        return find(c[x][1],rk-sz);
    }
    
    char op[5];
    int main()
    {
        n=read();m=read();
        for(int i=1;i<=n;i++)bel[i]=rt[i]=i,size[i]=1;
        for(int i=1;i<=n;i++)s[i]=read();
        for(int i=1;i<=m;i++)
        {
            int x=read(),y=read();
            if(bel[x]!=bel[y]) Merge(x,y);
        }
        m=read();
        for(int i=1;i<=m;i++)
        {
            scanf("%s",op+1);int x=read(),y=read();
            if(op[1]=='Q') printf("%d
    ",size[rt[bel[x]]]<y?-1:find(rt[bel[x]],y));
            else if(bel[x]!=bel[y]) Merge(x,y);
        }
        return 0;
    }
  • 相关阅读:
    Java 最常见的 208 道面试题(第九模块和第十模块答案)
    更灵活的边缘云原生运维:OpenYurt 单元化部署新增 Patch 特性
    高德 Serverless 平台建设及实践
    官宣:恭喜 ChaosBlade 项目进入 CNCF Sandbox
    工商银行分布式服务 C10K 场景解决方案
    云原生的进一步具象化
    阿里云入选 2021 Gartner APM 魔力象限,国内唯一入选云厂商
    云原生下的灰度体系建设
    seata-golang 一周年回顾
    WebAssembly + Dapr = 下一代云原生运行时?
  • 原文地址:https://www.cnblogs.com/FallDream/p/bzoj2733.html
Copyright © 2020-2023  润新知