題意:給一個矩陣,每次詢問一個子矩陣裏面的第小的數。
如果它只有一個詢問,直接排個序就行,考慮如何用樹狀數組來操作。也就是樹狀數組如何查詢排名。
具體實現就是:每次二分一個值,然後把小於該值的點全部加入二位樹狀數組裏,查這個矩形的時候直接判一下前綴和即可。
接下來考慮如何把如上方法轉移到多組詢問裏。顯然可以整體二分。
注意二分的是位置,返回排完序的元素裏面這個位置的值來判定即可,省去了離散化。
/*
考慮如何使用整體二分求解。
二分這個位置,然後把小於這些數值的點染黑加入二位樹狀數組裏,根據前綴和判定答案大小,把答案小的和答案大的分開來處理即可。
爲了保證時間複雜度的正確性,每次分成兩半遞歸時去掉所有貢獻,爲那些大於mid的值記一下上次的值
*/
#include <bits/stdc++.h>
#define regi register int
int n,Q;
int ans[60101];
int id[60101];
int last[60101];
int c[511][511];
int tmpleft[60101];
int tmpright[60101];
int nn;
struct Matrix{
int x;
int y;
int v;
}ma[500001];
struct Question{
int lx;
int ly;
int rx;
int ry;
int k;
}q[60101];
inline int read(){
int r=0,w=0,c;
for(;!isdigit(c=getchar());r=c);
for(w=c^48;isdigit(c=getchar());w=w*10+(c^48));
return r^45?w:-w;
}
bool cmp(Matrix x,Matrix y){
return x.v<y.v;
}
int add(int x,int y,int v){
for(regi i=x;i<=n;i+=i&-i)
for(regi j=y;j<=n;j+=j&-j)
c[i][j]+=v;
}
int ask(int x,int y){
int s=0;
for(regi i=x;i>0;i-=i&-i)
for(regi j=y;j>0;j-=j&-j)
s+=c[i][j];
return s;
}
int return_more(int lx,int ly,int rx,int ry){
return ask(rx,ry)-ask(lx-1,ry)-ask(rx,ly-1)+ask(lx-1,ly-1);
}
void solve(int l,int r,int ql,int qr){
if(ql>qr)
return;
if(l==r){
for(regi i=ql;i<=qr;++i)
ans[id[i]]=ma[l].v;
return;
}
int mid=l+r>>1;
for(regi i=l;i<=mid;++i)
add(ma[i].x,ma[i].y,1);
int tl=0,tr=0;
for(regi i=ql;i<=qr;++i){
int X=id[i];
regi S=last[X]+return_more(q[X].lx,q[X].ly,q[X].rx,q[X].ry);
if(S>=q[X].k)
tmpleft[++tl]=X;
else{
tmpright[++tr]=X;
last[X]=S;
}
}
for(regi i=1;i<=tl;++i)
id[ql+i-1]=tmpleft[i];
for(regi i=1;i<=tr;++i)
id[ql+tl+i-1]=tmpright[i];
for(regi i=l;i<=mid;++i)
add(ma[i].x,ma[i].y,-1);
solve(l,mid,ql,ql+tl-1);
solve(mid+1,r,ql+tl,qr);
}
main(){
n=read();
Q=read();
for(regi i=1;i<=n;++i)
for(regi j=1;j<=n;++j)
ma[++nn]={i,j,read()};
std::sort(ma+1,ma+nn+1,cmp);
for(regi i=1;i<=Q;++i){
q[i].lx=read();
q[i].ly=read();
q[i].rx=read();
q[i].ry=read();
q[i].k=read();
}
for(regi i=1;i<=Q;++i)
id[i]=i;
solve(1,nn,1,Q);
for(regi i=1;i<=Q;++i)
printf("%d\n",ans[i]);
return 0;
}