[Tyvj 1730] 二逼平衡树

时间:2021-07-10 17:44:16

先来一发题面QwQ

[TYVJ1730]二逼平衡树

Time Limit:2 s   Memory Limit:512 MB

Description

您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:
1.查询k在区间内的排名
2.查询区间内排名为k的值
3.修改某一位值上的数值
4.查询k在区间内的前驱(前驱定义为小于x,且最大的数)
5.查询k在区间内的后继(后继定义为大于x,且最小的数)

Input

第一行两个数 n,m 表示长度为n的有序序列和m个操作
第二行有n个数,表示有序序列
下面有m行,opt表示操作标号
若opt=1 则为操作1,之后有三个数l,r,k 表示查询k在区间[l,r]的排名
若opt=2 则为操作2,之后有三个数l,r,k 表示查询区间[l,r]内排名为k的数
若opt=3 则为操作3,之后有两个数pos,k 表示将pos位置的数修改为k
若opt=4 则为操作4,之后有三个数l,r,k 表示查询区间[l,r]内k的前驱
若opt=5 则为操作5,之后有三个数l,r,k 表示查询区间[l,r]内k的后继

Output

对于操作1,2,4,5各输出一行,表示查询结果

Sample Input

9 6
4 2 2 1 9 4 0 1 1
2 1 4 3
3 4 10
2 1 4 3
1 2 5 9
4 3 9 5
5 2 8 5

Sample Output

2
4
3
4
9

Hint

n,m<=50000   保证有序序列所有值在任何时刻满足[0,10^8]但是询问的数未必

这题正解似乎是树套树(线段树套平衡树)

然而我用分块大法水过去了233

分块的话思路还是比较明确的,比如对于求排名操作(操作1),对于两端的块我们可以选择直接暴力查询比$key$值小的值的个数,中间的完整块可以选择二分查找($std::lower\_bound$)来得出比$key$小的元素个数,累加起来加上1就是答案了.

求K大的话比较麻烦,要二分第K大的值然后用求排名操作来检验...

修改直接暴力修改不解释

前驱/后继则可以用上面求排名的方式来查找,不同的是对于比$key$小/大的元素不应累加而是求最大/小值.

然后就到了袋马时间~(突然感觉好草率啊QwQ)

GitHub:BlockSearch

 #include <cmath>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm> const int MAXN=;
const int MAXB=;
const int INF=0x7FFFFFFF; int n,m;
int endSize;
int blockNum;
int blockSize;
int data[MAXN];
int block[MAXN][MAXB]; void Initialize();
void FastRead(int&);
void Modify(int,int);
int Kth(int,int,int);
int Rank(int,int,int);
int Predecessor(int,int,int);
int Successor(int,int,int); int main(){
int opt,l,r,num;
Initialize();
for(int i=;i<m;i++){
scanf("%d",&opt);
if(opt==){
FastRead(l);
FastRead(r);
FastRead(num);
printf("%d\n",Rank(l-,r-,num));
}
else if(opt==){
FastRead(l);
FastRead(r);
FastRead(num);
printf("%d\n",Kth(l-,r-,num));
}
else if(opt==){
FastRead(l);
FastRead(num);
Modify(l-,num);
}
else if(opt==){
FastRead(l);
FastRead(r);
FastRead(num);
printf("%d\n",Predecessor(l-,r-,num));
}
else{
FastRead(l);
FastRead(r);
FastRead(num);
printf("%d\n",Successor(l-,r-,num));
}
}
return ;
} inline int Rank(int l,int r,int key){
int lb=l/blockSize;
int rb=r/blockSize;
int ans=;
if(lb==rb){
for(int i=l;i<=r;i++){
if(data[i]<key){
ans++;
}
}
}
else{
for(int i=l;i<(lb+)*blockSize;i++){
if(data[i]<key){
ans++;
}
}
for(int i=rb*blockSize;i<=r;i++){
if(data[i]<key){
ans++;
}
}
for(int i=lb+;i<rb;i++){
ans+=std::lower_bound(block[i],block[i]+blockSize,key)-block[i];
}
}
return ans;
} inline int Kth(int l,int r,int pos){
int L=-;
int R=;
while(R-L>){
int mid=(L+R)>>;
if(Rank(l,r,mid)>pos)
R=mid;
else
L=mid;
}
return R-;
} inline void Modify(int pos,int key){
if(data[pos]==key)
return;
int* thisBlock=block[pos/blockSize];
int old=data[pos];
int size=(pos/blockSize)==blockNum?endSize:blockSize;
data[pos]=key;
pos=std::lower_bound(thisBlock,thisBlock+size,old)-thisBlock;
thisBlock[pos]=key;
std::sort(thisBlock,thisBlock+size);
} inline int Predecessor(int l,int r,int key){
int lb=l/blockSize;
int rb=r/blockSize;
int ans=-INF;
if(lb==rb){
for(int i=l;i<=r;i++){
if(data[i]<key)
ans=std::max(ans,data[i]);
}
}
else{
for(int i=l;i<(lb+)*blockSize;i++){
if(data[i]<key)
ans=std::max(ans,data[i]);
}
for(int i=rb*blockSize;i<=r;i++){
if(data[i]<key)
ans=std::max(ans,data[i]);
}
for(int i=lb+;i<rb;i++){
int pos=std::lower_bound(block[i],block[i]+blockSize,key)-block[i];
if(pos>)
ans=std::max(ans,block[i][pos-]);
}
}
return ans;
} inline int Successor(int l,int r,int key){
int lb=l/blockSize;
int rb=r/blockSize;
int ans=INF;
if(lb==rb){
for(int i=l;i<=r;i++)
if(data[i]>key)
ans=std::min(ans,data[i]);
}
else{
for(int i=l;i<(lb+)*blockSize;i++){
if(data[i]>key)
ans=std::min(ans,data[i]);
}
for(int i=rb*blockSize;i<=r;i++){
if(data[i]>key)
ans=std::min(ans,data[i]);
}
for(int i=lb+;i<rb;i++){
int pos=std::upper_bound(block[i],block[i]+blockSize,key)-block[i];
if(pos<blockSize)
ans=std::min(ans,block[i][pos]);
}
}
return ans;
} void Initialize(){
#ifndef ASC_LOCAL
freopen("psh.in","r",stdin);
freopen("psh.out","w",stdout);
#endif
FastRead(n);
FastRead(m);
blockSize=sqrt(n);
for(int i=;i<n;i++){
FastRead(data[i]);
block[blockNum][endSize]=data[i];
if(++endSize==blockSize){
blockNum++;
endSize=;
}
}
for(int i=;i<blockNum;i++)
std::sort(block[i],block[i]+blockSize);
if(endSize!=)
std::sort(block[blockNum],block[blockNum]+endSize);
} inline void FastRead(int& target){
target=;
register char ch=getchar();
bool neg=false;
while(!isdigit(ch)){
if(ch=='-')
neg=true;
ch=getchar();
}
while(isdigit(ch)){
target=target*+ch-'';
ch=getchar();
}
if(neg)
target=-target;
}

Backup: Block Search

然后图包时间233(拿图至少评论一下吼不吼哇QwQ)

[Tyvj 1730] 二逼平衡树