NKOJ P2670 动态区间第K小数【分块】

这道题呢其实也是一个毒瘤….调了好久的代码这道题…..让我意识到自己对于stl的很多东西都还是不熟(比如 l o w e r b o u n d ,于是我就手写了一个二分查找)。

当然这道题还是确实让我见识到了分块的强大….动态区间第 K 小数普遍的方法就是各种主席树,树套树来做,但是分块就除去了写各种树产生的不必要的麻烦(其实这算是我第一次真正写分块中间有很多地方细节最开始都没有注意)。

正题 :

首先我们先考虑一下将已知的数组 A [ ] 按照常规操作进行分块:将 A [ ] 分为 n 块,每块 n 个数,然后我们就将每块中的数字从小到大排序得到一个新数组 B [ ]

我们用 A [ ] 记录原数列, B [ i ] 记录排序后第 i 块的数字。

分好块了,接下来让我们考虑一下修改。由于题意的修改是直接将原数列的某一个数修改为新数,所以我们直接 A [ x ] = y 即可,但是与此相对应的 B [ ] 也需要修改,所以我们就用二分查找找到原 A [ x ] B [ ] 中对应的 B [ L o c ] ,进行修改 B [ L o c ] = y 。但是这样修改后我们发现原来排好序的有序数列有可能被打乱了,这个时候我们就只需要重新排序即可。

考虑完了修改,就再来考虑一下查询 [ x , y ]

如果 [ x , y ] 位于同一个块当中,那么我们就只需要暴力枚举即可,问题其实在于 [ x , y ] 位于多个块当中的时候我们应该如何操作。考虑一下二分答案:我们不妨二分一个 m i d 值,并且假设我们已经知道每个块当中有多少个数比 m i d 小,于是我们就可以得到:若 [ x , y ] 中比 m i d 小的数字有 k 1 个,那么此时的 m i d 就是第 k 小数,输出即可,如果比 m i d 小的数字大于 k 1 个,那我们就减小 m i d ,如果大于,我们就增大 m i d

那么问题来了,我们应该如何统计块中比 m i d 小的数字有多少个呢?注意到,我们前面已经维护了每个块的有序性,所以我们就只需要二分查找进行确定 m i d 在每一个块当中应占的位置即可。

参考代码:

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define DB double
#define SG string
#define LL long long
using namespace std;
const int Max=5e4+5;
const int Mod=1e9+7;
const int Inf=1e18;
char CH[3];
LL N,M,S,SNum,A[Max],B[Max],L[Max],R[Max];
inline int Read(){
    int X=0;char CH=getchar();bool F=0;
    while(CH>'9'||CH<'0'){if(CH=='-')F=1;CH=getchar();}
    while(CH>='0'&&CH<='9'){X=(X<<1)+(X<<3)+CH-'0';CH=getchar();}
    return F?-X:X;
}
inline void Write(int X){
    if(X<0)X=-X,putchar('-');
    if(X>9)Write(X/10);
    putchar(X%10+48);
}
void Update(){
    LL X=Read(),Y=Read();
    LL I,Fx=(X-1)/S+1;
    LL Loc=lower_bound(B+L[Fx],B+1+R[Fx],A[X])-B;
    A[X]=Y;B[Loc]=Y;sort(B+L[Fx],B+1+R[Fx]);
}
LL Find(LL X,LL Num){
    LL Left=L[X],Right=R[X];
    while(Left<=Right){
        LL Mid=Left+Right>>1;
        if(B[Mid]>Num){
            Right=Mid-1;
        } else if (B[Mid]<Num){
            Left=Mid+1;
        } else {
            return Mid;
        }
    }
    return Left;
}
LL Calc(LL X,LL Y,LL Num){
    LL K,Fx=(X-1)/S+1,Fy=(Y-1)/S+1;
    LL Cnt=0;
    if(Fx==Fy){
        for(K=X;K<=Y;K++){
            if(A[K]<Num){
                Cnt++;
            }
        }
    } else {
        for(K=X;K<=R[Fx];K++){
            if(A[K]<Num){
                Cnt++;
            }
        }
        for(K=L[Fy];K<=Y;K++){
            if(A[K]<Num){
                Cnt++;
            }
        }
        for(K=Fx+1;K<Fy;K++){
            LL Get=Find(K,Num);
            while(Get-1>=L[K]&&B[Get-1]==B[Get]){
                Get--;
            }Cnt=Cnt+Get-L[K];
        }
    }
    return Cnt;
}
LL GetAns(){
    LL X=Read(),Y=Read(),K=Read();
    LL Mid,Left=0,Right=50005;
    while(Left+1<Right){
        Mid=Left+Right>>1;
        if(Calc(X,Y,Mid)>=K){
            Right=Mid;
        } else {
            Left=Mid;
        }
    }
    Write(Left),putchar('\n');
}
int main(){
    LL I,J,K;
    N=Read(),M=Read();
    S=sqrt(N);SNum=N/S;
    if(S*SNum!=N){
        SNum++;
    }
    for(I=1;I<=N;I++){
        A[I]=B[I]=Read();
    }
    for(I=1;I<=SNum;I++){
        L[I]=(I-1)*S+1;R[I]=I*S;
    }R[SNum]=N;
    for(I=1;I<=SNum;I++){
        sort(B+L[I],B+1+R[I]);
    }
    for(I=1;I<=M;I++){
        scanf("%s",CH);
        if(CH[0]=='Q'){
            GetAns();
        } else {
            Update();
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/yanzhenhuai/article/details/80993321