分块过了,甚至于是有问题的分块都能过。
我的做法是分三个块,第一个块是位置块,第二三个块是权值块。对三个块分别块内排序,位置块块内按照数的大小排序。我的错误是:对于询问 [l,r],若 [l,r] 在同一个位置块或者在相邻位置块,直接块内找到第 k 小。而由于我仅进行了块内排序,所以不能对相邻位置块直接找块内第 k 小。
代码见下,这是改完后的正确代码,原有的错误在第 99 行,注释掉了:
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
typedef long long ll;
inline int read(){
int x=0;
int f=0,ch=0;
while(ch<48||ch>57) f=(ch=='-'),ch=getchar();
while(ch>47&&ch<58) x=(x<<3)+(x<<1)+(ch&15),ch=getchar();
return f?-x:x;
}
inline void write(ll x,char end='\n'){
if(x==0){
putchar('0');
putchar(end);
return;
}
if(x<0) putchar('-'),x=-x;
int ch[70]={0},cnt=0;
while(x){
ch[cnt++]=(int)(x%10);
x/=10;
}
while(cnt--) putchar(ch[cnt]+48);
putchar(end);
}
const int N=2e5+5;
const int S=450;
int n,m,s,B;
struct node{
int x,id;
friend bool operator<(node x,node y){
return x.x<y.x;
}
}d[N];
int a[N];
int st[S],ed[S];
int bel[N];
int start[S][S];
node block1[N],block2[N];
node block3[N];
//按大小排序,每个块内按原顺序排序
inline bool cmp2(node x,node y){
return x.id<y.id;
}
inline void initblock(){
int T=sqrt(n);
for(int i=1;i<=n;++i){
bel[i]=(i-1)/T+1;
if(!st[bel[i]])
st[bel[i]]=i;
ed[bel[i]]=i;
}
B=bel[n];
for(int i=1;i<=n;++i)
block1[i]=d[i];
for(int i=1;i<=B;++i){
sort(block1+st[i],block1+ed[i]+1);
}
sort(d+1,d+n+1);
for(int i=1;i<=n;++i)
block2[i]=block3[i]=d[i];
for(int i=1;i<=B;++i){
sort(block2+st[i],block2+ed[i]+1,cmp2);
}
for(int i=1;i<=B;++i){
for(int j=st[i];j<=ed[i];++j){
int to=bel[block2[j].id];
if(!start[i][to]) start[i][to]=j;
}
start[i][B+1]=ed[i]+1;
for(int j=B;j>=1;--j){
if(!start[i][j]) start[i][j]=start[i][j+1];
}
}
}
inline void find(int x,int y,int l,int r,int k){
for(int i=st[x];i<=ed[y];++i){
if(block1[i].id>=l&&block1[i].id<=r) --k;
if(k==0){
write(block1[i].x);
break;
}
}
}
inline void find2(int x,int y,int l,int r,int k){
for(int i=st[x];i<=ed[y];++i){
if(block3[i].id>=l&&block3[i].id<=r) --k;
if(k==0){
write(block3[i].x);
break;
}
}
}
inline void solve(){
int l=read(),r=read(),k=read();
int x=bel[l],y=bel[r];
if(x==y){//这里原来写的是x+1>=y,在这道题可以过,但是SPOJ的第一个点就挂了
find(x,y,l,r,k);
return;
}
for(int i=1;i<=B;++i){
int g=start[i][y]-start[i][x+1];
for(int j=start[i][x+1]-1;j>=start[i][x];--j){
if(block2[j].id>=l&&block2[j].id<=r) ++g;
}
for(int j=start[i][y];j<start[i][y+1];++j){
if(block2[j].id>=l&&block2[j].id<=r) ++g;
}
if(g>=k){
find2(i,i,l,r,k);
break;
}
else k-=g;
}
}
int main(){
n=read(),m=read();
for(int i=1;i<=n;++i) a[i]=read(),d[i]={a[i],i};
initblock();
while(m--)
solve();
return 0;
}