题目
给你一个N*N的矩阵,不用算矩阵乘法,但是每次询问一个子矩形的第K小数。
思路
整体二分。
整体二分主要适用于对于二分状态的改变,可以在可接受的复杂度内修改的题目。
就本题而言,二分答案,如果考虑将[1,mid]的区间内的点加入树状数组中,在二维平面上标记为1,然后比较每个询问与K的关系,接着将询问分组,接着二分。
以上应该是整体二分的基本模型。
代码
#include<bits/stdc++.h>
#define M 60005
using namespace std;
struct node{
int x,y,val;
bool operator < (const node& res)const{
return val<res.val;
}
}B[505*505];
int n,Q,A[505][505],bcnt,ans[M],t1[M],t2[M];
int id[M],now;
struct que{
int x1,y1,x2,y2,k;
}wk[M];
struct BIT{
int C[505][505];
void Add(int x,int y,int d){
x++;y++;
while(x<=n+1){
int a=y;
while(y<=n+1){
C[x][y]+=d;
y+=(y&-y);
}
y=a;
x+=(x&-x);
}
}
int sum(int x,int y){
x++;y++;
int res=0;
while(x){
int a=y;
while(y){
res+=C[x][y];
y-=(y&-y);
}
y=a;
x-=(x&-x);
}
return res;
}
}T;
void update(int x,int f){
T.Add(B[x].x,B[x].y,f);
}
int query(int x1,int y1,int x2,int y2){
return T.sum(x2,y2)-T.sum(x1-1,y2)-T.sum(x2,y1-1)+T.sum(x1-1,y1-1);
}
void solve(int l,int r,int ql,int qr){
if(l>r)return;
if(ql==qr){
for(int i=l;i<=r;i++)ans[id[i]]=ql;
return;
}
int mid=(ql+qr)>>1,c1=0,c2=0,c=l-1;
while(now<mid)update(++now,1);
while(now>mid)update(now--,-1);
for(int i=l;i<=r;i++){
if(query(wk[id[i]].x1,wk[id[i]].y1,wk[id[i]].x2,wk[id[i]].y2)<wk[id[i]].k)t1[++c1]=id[i];
else t2[++c2]=id[i];
}
for(int i=1;i<=c2;i++)id[++c]=t2[i];
for(int i=1;i<=c1;i++)id[++c]=t1[i];
solve(l,l+c2-1,ql,mid);solve(l+c2,r,mid+1,qr);
}
int main(){
// freopen("1.in","r",stdin);
// freopen("1.ans","w",stdout);
scanf("%d%d",&n,&Q);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
scanf("%d",&A[i][j]),B[++bcnt].val=A[i][j],B[bcnt].x=i,B[bcnt].y=j;
sort(B+1,B+bcnt+1);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++){
int l=1,r=bcnt,res=-1;
while(l<=r){
int mid=(l+r)>>1;
if(B[mid].val>=A[i][j]){
res=mid;
r=mid-1;
}
else l=mid+1;
}
A[i][j]=res;
}
for(int i=1;i<=Q;i++){
scanf("%d%d%d%d%d",&wk[i].x1,&wk[i].y1,&wk[i].x2,&wk[i].y2,&wk[i].k);
id[i]=i;
}
solve(1,Q,1,bcnt);
for(int i=1;i<=Q;i++){
printf("%d
",B[ans[i]].val);
}
return 0;
}