caioj1442:第k小的数Ⅱ

Posted Star_Feel

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了caioj1442:第k小的数Ⅱ相关的知识,希望对你有一定的参考价值。

【传送门:caioj1442


简要题意:

  给出n个点,每个点都有一个权值,m个操作,操作有两种:第一种是询问l到r的第k小的值,然后输出这个值,第二种是将第x个点的值改为k


题解:

  又是一道主席树的例题,不过简直比前两题(caioj1441,caioj1443)难不止一点点

  看到第一种操作,我们可以用主席树来搞,但是第二种操作的话,我们就要用树状数组来维护每个第二种操作所带来的影响,而且每次要求第一种操作的时候都要先在树状数组中找到相应的点,然后再处理主席树


参考代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<cmath>
using namespace std;
int a[210000];
struct node
{
    int lc,rc,c;
}tr[3100000];int cnt;
int rt[210000];int n;
int ust[210000];//树状数组 
int lowbit(int x)//树状数组用来找上司或下属 
{
    return x&-x;
}
void Link(int &u,int l,int r,int p,int c)
{
    if(u==0) u=++cnt;
    tr[u].c+=c;
    if(l==r) return ;
    int mid=(l+r)/2;
    if(p<=mid) Link(tr[u].lc,l,mid,p,c);
    else Link(tr[u].rc,mid+1,r,p,c);
}
void Merge(int &u1,int u2)
{
    if(u1==0){u1=u2;return ;}
    if(u2==0) return ;
    tr[u1].c+=tr[u2].c;
    Merge(tr[u1].lc,tr[u2].lc);
    Merge(tr[u1].rc,tr[u2].rc);
}
void Turn(int u,int c)//树状数组的实时更新 
//c==-1时树状数组记录每个点所在线段树的根
//c==0时记录左孩子
//c==1时记录右孩子 
{
    while(u>=n+1)
    {
        if(c==-1) ust[u]=rt[u];
        else if(c==0) ust[u]=tr[ust[u]].lc;
        else if(c==1) ust[u]=tr[ust[u]].rc;
        u-=lowbit(u);
    }
}
void Modefy(int u,int p,int c)//对于修改值就用树状数组来节约时间 
{
    while(u<=2*n)
    {
        Link(rt[u],0,1000000000,p,c);
        u+=lowbit(u);
    }
}
int Getsum(int u)//求出修改过后树状数组得到的值 
{
    int ret=0;
    while(u>=n+1)
    {
        ret+=tr[tr[ust[u]].lc].c;
        u-=lowbit(u);
    }
    return ret;
}
int Ask(int u1,int u2,int p1,int p2,int l,int r,int p)
{
    if(l==r) return l;
    int c=tr[tr[u2].lc].c-tr[tr[u1].lc].c+Getsum(p2+n)-Getsum(p1+n);
    int mid=(l+r)/2;
    if(p<=c)
    {
        Turn(p1+n,0);
        Turn(p2+n,0);
        return Ask(tr[u1].lc,tr[u2].lc,p1,p2,l,mid,p);
    }
    else
    {
        Turn(p1+n,1);
        Turn(p2+n,1);
        return Ask(tr[u1].rc,tr[u2].rc,p1,p2,mid+1,r,p-c);
    }
}
int main()
{
    int m;
    scanf("%d%d",&n,&m);
    cnt=0;memset(rt,0,sizeof(rt));
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        Link(rt[i],0,1000000000,a[i],1);
        Merge(rt[i],rt[i-1]);
    }
    char st[2];
    for(int i=1;i<=m;i++)
    {
        scanf("%s",st+1);
        if(st[1]==\'Q\')
        {
            int l,r,k;
            scanf("%d%d%d",&l,&r,&k);
            Turn(l+n-1,-1);
            Turn(r+n,-1);
            printf("%d\\n",Ask(rt[l-1],rt[r],l-1,r,0,1000000000,k));
        }
        else
        {
            int p,c;
            scanf("%d%d",&p,&c);
            Modefy(p+n,a[p],-1);
            a[p]=c;
            Modefy(p+n,a[p],1);
        }
    }
    return 0;
}

 

 

 

以上是关于caioj1442:第k小的数Ⅱ的主要内容,如果未能解决你的问题,请参考以下文章

POJ 1442 优先队列 模板

快排划分思想的应用-求第k大数或者第k小的数(求前k大数或者前k小的数)

快排划分思想的应用-求第k大数或者第k小的数(求前k大数或者前k小的数)

7-2 找第k小的数 (20分) —— 分治算法

9.27 在两个排序数组中找到第K小的数

选择问题(选择数组中第K小的数)