zoj2112(单点修改区间第K小)

我又来填坑了哈哈哈!!!!

     
     

Description:

    有一个初始序列,长度为 N(N<=50000)

    两种操作,操作个数为 M(M<=10000)

       1.询问一个区间里第k小的数;

       2.修改数列中一个数的值。

   
   
   

Solution:

    首先如果不带修改的话,就是直接上主席树就行了,主席树做区间第 k 大在这里就不再赘述了。

   但是此题是带有单点修改,那么可以采用树状数组,本来主席树是存的 1T 的值域线段树,那么树状数组套主席树就相当于存的是 Tlowbit(T)+1T 的值域线段树,然后如果要求 [L,R] 的答案就可以按树状数组的方法做了。之后就是修改操作,只需要按树状数组的修改方法就行了,很简单。坑爹的是此题卡空间 QAQ ,空间是 O((N+M)log22N) ,荣幸的 MLE ,所以我们考虑先建一棵主席树表示原序列,然后再建一棵树状数组套主席树存修改,那么空间是 O(Nlog2N+Mlog22N)

    然后就愉快的 AC 了。
   
   
   

Code:

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <iostream>

using namespace std;

int N;
int M;
int T;

struct tree_
{
    int son[2];
    int sum;
}tree[2000010]={{{0},0}};
struct cz_
{
    char ch;
    int a1,a2,a3;
}cz[10010]={{'\0',0,0,0}};
int tp=0;
int root1[50010]={0},root2[50010]={0};
int a[50010]={0};
int L[50010]={0},R[50010]={0};
int Lp=0,Rp=0;
int val[60010]={0},vp=0;

void read_char(char &ch)
{
    ch=getchar();
    for(;ch==' ' || ch=='\n' || ch=='\r' || ch=='\0';ch=getchar());
    return;
}

int lowbit(int x)
{return x&(-x);}

int check(int k)
{
    for(int l=1,r=vp;l<=r;)
    {
        int mid=(l+r)>>1;
        if(val[mid]==k)
            return mid;
        if(val[mid]<k)
            l=mid+1;
        else r=mid-1;
    }
    return 0;
}

void Pre_(int l,int r,int k)
{
    if(l==r) return;
    int mid=(l+r)>>1;
    tree[k].son[0]=++tp;
    Pre_(l,mid,tree[k].son[0]);
    tree[k].son[1]=++tp;
    Pre_(mid+1,r,tree[k].son[1]);
    return;
}

void Clear(int x,int *G,int &Gp)
{
    Gp=0;
    for(;x>=1;x-=lowbit(x))
        G[++Gp]=root2[x];
    return;
}

void Change(int New,int Old,int l,int r,int o,int add)
{
    tree[New]=tree[Old];tree[New].sum+=add;
    if(l==r) return;
    int mid=(l+r)>>1;
    int gg=(o>mid);
    tree[New].son[gg]=++tp;
    if(gg==0) Change(tree[New].son[gg],tree[Old].son[gg],l,mid,o,add);
    else Change(tree[New].son[gg],tree[Old].son[gg],mid+1,r,o,add);
    return;
}

int Find(int cnt1,int cnt2,int k,int l,int r)
{
    if(l==r) return l;
    int mid=(l+r)>>1;
    int sum=tree[tree[cnt2].son[0]].sum-tree[tree[cnt1].son[0]].sum;
    for(int i=1;i<=Rp;i++)
        sum+=tree[tree[R[i]].son[0]].sum;
    for(int i=1;i<=Lp;i++)
        sum-=tree[tree[L[i]].son[0]].sum;
    int gg=0;
    if(sum<k)
        gg=1,k-=sum;
    for(int i=1;i<=Rp;i++)
        R[i]=tree[R[i]].son[gg];
    for(int i=1;i<=Lp;i++)
        L[i]=tree[L[i]].son[gg];
    if(gg==0) return Find(tree[cnt1].son[gg],tree[cnt2].son[gg],k,l,mid);
    return Find(tree[cnt1].son[gg],tree[cnt2].son[gg],k,mid+1,r);
}

int main()
{
    scanf("%d",&T);
    for(;T>0;T--)
    {
        tp=vp=0;
        scanf("%d%d",&N,&M);
        root1[0]=++tp;
        for(int i=1;i<=N;i++)
        {
            scanf("%d",&a[i]);
            val[++vp]=a[i];
        }
        for(int i=1;i<=M;i++)
        {
            read_char(cz[i].ch);
            if(cz[i].ch=='Q') scanf("%d%d%d",&cz[i].a1,&cz[i].a2,&cz[i].a3);
            else scanf("%d%d",&cz[i].a1,&cz[i].a2),val[++vp]=cz[i].a2;
        }
        sort(val+1,val+vp+1);
        int help=1;
        for(int i=2;i<=vp;i++)
            if(val[i]!=val[i-1])
                val[++help]=val[i];
        vp=help;
        Pre_(1,vp,root1[0]);
        for(int i=1;i<=N;i++)
        {
            root1[i]=++tp;
            int place=check(a[i]);
            Change(root1[i],root1[i-1],1,vp,place,1);
        }
        root2[0]=++tp;
        Pre_(1,vp,root2[0]);
        for(int i=1;i<=N;i++)
            root2[i]=root2[0];
        for(int i=1;i<=M;i++)
        {
            if(cz[i].ch=='Q')
            {
                int l,r,k;
                l=cz[i].a1,r=cz[i].a2,k=cz[i].a3;
                Clear(r,R,Rp);Clear(l-1,L,Lp);
                int tmp=Find(root1[l-1],root1[r],k,1,vp);
                printf("%d\n",val[tmp]);
            }
            else
            {
                int p,q,place=check(a[cz[i].a1]);
                p=cz[i].a1,q=check(cz[i].a2);
                for(int j=p;j<=N;j+=lowbit(j))
                {
                    int t=++tp;
                    Change(t,root2[j],1,vp,place,-1);
                    root2[j]=t;t=++tp;
                    Change(t,root2[j],1,vp,q,1);
                    root2[j]=t;
                }
                a[cz[i].a1]=cz[i].a2;
            }
        }
        memset(tree,0,sizeof(struct tree_)*(tp+10));
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值