題目大意:給出一個N*N的矩陣,每次詢問一個子矩形的第K小數。
整體二分即在二分答案的同時把詢問分到兩個集合中再分別二分直到每個詢問出解。
相當於把多個詢問一起二分
如何判斷一個數x是不是k小呢
把矩陣中的小於等於x的數設爲1,大於x的數設爲0,求詢問矩陣中的小於等於x的數有多少個,大於等於k說明x 有可能是k小。
#include <cstdio>
#include <algorithm>
#define N 505
#define M 60005
#define INF 1000000000
using namespace std;
int n,m;
namespace BIT {
inline int lowbit(int x) { return x&-x; }
int c[N][N];
int Sum(int x,int y) {
int tmp=0;
for(int i=x;i;i-=lowbit(i))
for(int j=y;j;j-=lowbit(j))
tmp+=c[i][j];
return tmp;
}
void Modify(int x,int y,int v) {
for(int i=x;i<=n;i+=lowbit(i))
for(int j=y;j<=n;j+=lowbit(j))
c[i][j]+=v;
return ;
}
}
using namespace BIT;
struct Data {
int x,y,v;
Data() {}
Data(int _x,int _y,int _v):x(_x),y(_y),v(_v) {}
bool operator < (const Data& rhs) const { return v<rhs.v; }
void Update(int mode) { Modify(x,y,mode); }
}a[N*N];
struct Query {
int ord,x1,y1,x2,y2,k;
void scan(int _ord) {
scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&k);
ord=_ord;
return ;
}
bool check() {
return Sum(x1-1,y1-1)+Sum(x2,y2)-Sum(x1-1,y2)-Sum(x2,y1-1)>=k;
}
}q[M],p[M];
int tot,now,ans[M];
void solve(int L,int R,int l,int r) {
if(L>R || l>r) return ;
int _L=L,_R=R,mid=l+r>>1;
while(now<tot && a[now+1].v<=mid) a[++now].Update(1);
while(now && a[now].v>mid) a[now--].Update(-1);
for(int i=L;i<=R;++i) {
if(q[i].check()) ans[q[i].ord]=mid, p[_L++]=q[i];
else p[_R--]=q[i];
}
for(int i=L;i<=R;++i) q[i]=p[i];
solve(L,_L-1,l,mid-1), solve(_R+1,R,mid+1,r);
return ;
}
int main() {
scanf("%d%d",&n,&m);
for(int i=1,x;i<=n;++i)
for(int j=1;j<=n;++j)
scanf("%d",&x), a[++tot]=Data(i,j,x);
sort(a+1,a+tot+1);
for(int i=1;i<=m;++i) q[i].scan(i);
solve(1,m,0,INF);
for(int i=1;i<=m;++i) printf("%d\n",ans[i]);
return 0;
}