zoukankan      html  css  js  c++  java
  • BZOJ2733:使用并查集维护连通性之后用线段树维护+线段树合并(动态开点)

    可以说是线段树合并的裸题吧

    题意就是给你两个操作

    一个操作是合并两个集合,这两个集合都是用权值线段树维护的,便于查询第k小元素

    另一个操作就是查询区间极值了

     1 #include<cstdio>
     2 const int maxn=100005;
     3 int n,m,sz;
     4 int v[maxn],id[maxn],fa[maxn],root[maxn];
     5 int lch[1800000],rch[1800000],sum[1800000];
     6 inline int read()
     7 {
     8     int x=0,f=1;char ch=getchar();
     9     while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
    10     while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    11     return x*f;
    12 }
    13 int find(int x)
    14 {
    15     return x==fa[x]?x:fa[x]=find(fa[x]);
    16 }
    17 void insert(int &k,int l,int r,int val)
    18 {
    19     if(k==0) k=++sz;
    20     if(l==r)
    21     {
    22         sum[k]=1;return;
    23     }
    24     int mid=(l+r)>>1;
    25     if(val<=mid) insert(lch[k],l,mid,val);
    26     else insert(rch[k],mid+1,r,val);
    27     sum[k]=sum[lch[k]]+sum[rch[k]];
    28 }
    29 int query(int k,int l,int r,int rank)
    30 {
    31     if(l==r) return l;
    32     int mid=(l+r)>>1;
    33     if(sum[lch[k]]>=rank) return query(lch[k],l,mid,rank);
    34     else return query(rch[k],mid+1,r,rank-sum[lch[k]]);
    35 }
    36 int merge(int x,int y)
    37 {
    38     if(x==0) return y;
    39     if(y==0) return x;
    40     lch[x]=merge(lch[x],lch[y]);
    41     rch[x]=merge(rch[x],rch[y]);
    42     sum[x]=sum[lch[x]]+sum[rch[x]];
    43     return x;
    44 }
    45 int main()
    46 {
    47     n=read();m=read();
    48     for(int i=1;i<=n;i++) v[i]=read();
    49     for(int i=1;i<=n;i++) fa[i]=i;
    50     int x,y;
    51     for(int i=1;i<=m;i++)
    52     {
    53         x=read(),y=read();
    54         int p=find(x),q=find(y);
    55         fa[p]=q;
    56     }
    57     for(int i=1;i<=n;i++)
    58     {
    59         insert(root[find(i)],1,n,v[i]);  //往对应的线段树插点
    60         id[v[i]]=i; 
    61     }
    62     int k=read();
    63     char ch[2];
    64     while(k--)
    65     {
    66         scanf("%s",ch);
    67         x=read();y=read();
    68         if(ch[0]=='Q')
    69         {
    70             int p=find(x);
    71             if(sum[root[p]]<y)
    72             {
    73                 puts("-1");continue;//查询越界 
    74             }
    75             int t=query(root[p],1,n,y);  //得到location 
    76             printf("%d
    ",id[t]);
    77         }
    78         else
    79         {
    80             int p=find(x),q=find(y);
    81             if(p!=q)
    82             {
    83                 fa[p]=q;
    84                 root[q]=merge(root[p],root[q]);
    85             }
    86         } 
    87     }
    88     return 0;
    89 }

    权值线段树的理解更加深刻了

    权值线段树的下标是数字本身,而存的是这个数出现的次数,也就是权值

    一般权值线段树都是和动态开点捆绑在一起的

    所谓动态开点,就是每个节点用的时候再开,可以去掉许多无用的节点

    和主席树的区别,目前阶段的理解就是,主席树需要离散化,动态开点线段树不需要?

    建n棵线段树,每一棵线段树维护[1,i]的数字出现情况

    也就是当前数字范围内的数出现了多少次

    然后前缀和查找就好了

    可以这么说,动态开点的权值线段树的儿子之间没有耦合,可持久化权值线段树的儿子之间是耦合在一起的

    虽然功能一样的,但是T和M会有差异

  • 相关阅读:
    创建100个文件,并对每个文件赋值
    vsftpd服务程序的三种认证模式
    slf4j打印日志必须的三个依赖包
    MySql镜像安装
    java子类调用父类构造器函数
    shell脚本批量调用接口
    go语言生成可执行文件
    linux通过VMware和主机相连连接互联网
    yum源配置
    vmware上安装linux过程记录
  • 原文地址:https://www.cnblogs.com/aininot260/p/9531044.html
Copyright © 2011-2022 走看看