ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

P2617 Dynamic Rankings【树状数组套主席树】

2020-06-29 17:51:34  阅读:257  来源: 互联网

标签:tmp cnt le 树状 int Rankings Dynamic ++ P2617


题意:

给定一个含有 \(n\) 个数的序列 \(a_1,a_2 \dots a_n\),需要支持两种操作:
Q l r k :表示查询下标在区间 \([l,r]\) 中的第 \(k\) 小的数;
C x y :表示将 \(a_x\) 改成 \(y\);
\(1\le n,m \le 10^5,1 \le l \le r \le n,1 \le k \le r-l+1,1\le 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\n",num[query(1,len,sv[i].k)]);
        }
    }
    return 0;
}

标签:tmp,cnt,le,树状,int,Rankings,Dynamic,++,P2617
来源: https://www.cnblogs.com/1024-xzx/p/13209220.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有