zoukankan      html  css  js  c++  java
  • P2617 Dynamic Rankings【树状数组套主席树】

    题意:

    给定一个含有 (n) 个数的序列 (a_1,a_2 dots a_n),需要支持两种操作:
    Q l r k :表示查询下标在区间 ([l,r]) 中的第 (k) 小的数;
    C x y :表示将 (a_x) 改成 (y)
    (1le n,m le 10^5,1 le l le r le n,1 le k le r-l+1,1le x le n,0 le a_i,y le 10^9)

    分析:
      对于第二种操作,可以直接用主席树解决。但对于单点修改,如果直接套用主席树,根据前缀和的思想,后面的每一棵树都要修改。复杂度巨大。因此,要借助树状数组来解决这个问题。在树状数组中,通过从位置 (p) 开始,向前每次 (+(p&(-p))),可以求出前 (p) 项的前缀和。此处,将树看成是点,用树状数组来维护树之间的关系,主席树来维护内部的结构。
      在权值上建立主席树,首先要离散化,把原来的序列和之后要更改成的值,存放在一个数组中。同时,把在线变成离线来处理。和一般的主席树不同,在每次要向主席树中加入一个新的值时,要先预处理出会影响的树的编号(利用树状数组的方法):

    void modify(int p,int pos,int w,int n)//树的序号,修改位置,值,树的总数
    {
        while(p<=n)
        {
            update(1,len,pos,w,root[p]);
            p+=(p&(-p));
        }
    }
    

    只对有影响的树进行修改。
      同时,在向一棵树中插入点时,并没有在前一棵树的基础上建立树。而是直接从该树的根开始,新建立一条链,在区间中对应的位置修改。按此操作对所有影响的数修改。

    void update(int l,int r,int pos,int w,int &cnt)
    {
        if(!cnt) cnt=++tol;
        tree[cnt].val+=w;
        if(l==r) return;
        int mid=(l+r)>>1;
        if(pos<=mid) update(l,mid,pos,w,tree[cnt].ls);
        else update(mid+1,r,pos,w,tree[cnt].rs);
    }
    

    查询时同样,先预处理出相应的树:

    void ask(int l,int r)
    {
        tmp[0][0]=0;
        tmp[1][0]=0;
        for(int i=r;i>0;i-=(i&(-i)))
            tmp[0][++tmp[0][0]]=root[i];
        for(int i=l-1;i>0;i-=(i&(-i)))
            tmp[1][++tmp[1][0]]=root[i];
    }
    

    然后,所有的树一起进行处理:

    int query(int l,int r,int k)
    {
        if(l==r) return l;
        int mid=(l+r)>>1,d=0;
        for(int i=1;i<=tmp[0][0];i++)
            d+=tree[tree[tmp[0][i]].ls].val;
        for(int i=1;i<=tmp[1][0];i++)
            d-=tree[tree[tmp[1][i]].ls].val;
        if(d>=k)
        {
            for(int i=1;i<=tmp[0][0];i++)
                tmp[0][i]=tree[tmp[0][i]].ls;
            for(int i=1;i<=tmp[1][0];i++)
                tmp[1][i]=tree[tmp[1][i]].ls;
            return query(l,mid,k);
        }
        else
        {
            for(int i=1;i<=tmp[0][0];i++)
                tmp[0][i]=tree[tmp[0][i]].rs;
            for(int i=1;i<=tmp[1][0];i++)
                tmp[1][i]=tree[tmp[1][i]].rs;
            return query(mid+1,r,k-d);
        }
    }
    

    代码:

    #include <bits/stdc++.h>
    
    using namespace std;
    const int N=1e5+5;
    int a[N],num[N<<1],tmp[2][N],root[N<<1];//原序列数组,用于离散化的数组,要计算的那logn个数组
    struct node
    {
        int val,ls,rs;
    }tree[N*150];//空间很大
    struct q
    {
        int l,r,k;
    }sv[N];//记录操作
    int tol,len;//点的个数,区间长度
    int index(int x)
    {
        return lower_bound(num+1,num+1+len,x)-num;
    }
    void update(int l,int r,int pos,int w,int &cnt)
    {
        if(!cnt) cnt=++tol;
        tree[cnt].val+=w;
        if(l==r) return;
        int mid=(l+r)>>1;
        if(pos<=mid) update(l,mid,pos,w,tree[cnt].ls);
        else update(mid+1,r,pos,w,tree[cnt].rs);
    }
    void modify(int p,int pos,int w,int n)//树的序号,修改位置,值,树的总数
    {
        while(p<=n)
        {
            update(1,len,pos,w,root[p]);
            p+=(p&(-p));
        }
    }
    int query(int l,int r,int k)
    {
        if(l==r) return l;
        int mid=(l+r)>>1,d=0;
        for(int i=1;i<=tmp[0][0];i++)
            d+=tree[tree[tmp[0][i]].ls].val;
        for(int i=1;i<=tmp[1][0];i++)
            d-=tree[tree[tmp[1][i]].ls].val;
        if(d>=k)
        {
            for(int i=1;i<=tmp[0][0];i++)
                tmp[0][i]=tree[tmp[0][i]].ls;
            for(int i=1;i<=tmp[1][0];i++)
                tmp[1][i]=tree[tmp[1][i]].ls;
            return query(l,mid,k);
        }
        else
        {
            for(int i=1;i<=tmp[0][0];i++)
                tmp[0][i]=tree[tmp[0][i]].rs;
            for(int i=1;i<=tmp[1][0];i++)
                tmp[1][i]=tree[tmp[1][i]].rs;
            return query(mid+1,r,k-d);
        }
    }
    void ask(int l,int r)
    {
        tmp[0][0]=0;
        tmp[1][0]=0;
        for(int i=r;i>0;i-=(i&(-i)))
            tmp[0][++tmp[0][0]]=root[i];
        for(int i=l-1;i>0;i-=(i&(-i)))
            tmp[1][++tmp[1][0]]=root[i];
    }
    int main()
    {
        int n,m,cnt=0;
        tol=0;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            num[++cnt]=a[i];
        }
        char op[4];
        for(int i=1;i<=m;i++)//离线
        {
            scanf("%s",op);
            if(strcmp(op,"C")==0)
            {
                scanf("%d%d",&sv[i].l,&sv[i].r);
                num[++cnt]=sv[i].r;
                sv[i].k=-1;
            }
            else
                scanf("%d%d%d",&sv[i].l,&sv[i].r,&sv[i].k);
        }
        //离散化:
        sort(num+1,num+1+cnt);
        len=unique(num+1,num+1+cnt)-num-1;//返回值是去重之后的尾地址
    //    cout<<"len="<<len<<endl;
        for(int i=1;i<=n;i++)
        {
            int pos=index(a[i]);//cout<<"pos="<<pos<<endl;
            modify(i,pos,1,n);
        }
        for(int i=1;i<=m;i++)
        {
            if(sv[i].k==-1)
            {
                int pos=index(a[sv[i].l]);
                modify(sv[i].l,pos,-1,n);
                a[sv[i].l]=sv[i].r;
                pos=index(sv[i].r);
                modify(sv[i].l,pos,1,n);
            }
            else
            {
                ask(sv[i].l,sv[i].r);
                printf("%d
    ",num[query(1,len,sv[i].k)]);
            }
        }
        return 0;
    }
    
    
  • 相关阅读:
    iOS:TabBarController 显示/隐藏第一级页面的TabBar
    iOS : Blur Effect
    ASIHTTPRequest / ASIFormDataRequest
    Xcode :Missing file warnings
    Axure设计软件下载安装及注册
    windows服务更改配置文件
    sql server 清理数据库日志
    sql server 随机生成布尔值
    sql server 授权相关命令
    用VS2015创建ASP.NET Web Forms 应用程序
  • 原文地址:https://www.cnblogs.com/1024-xzx/p/13209220.html
Copyright © 2011-2022 走看看