标算整体二分,然而窝太弱了并不会做。
分块大法好:狠狠点我
我们把n*n个数排序,然后从小到大插入矩阵,每次插n个,用前缀和维护每个子矩阵当前已经填了多少个数。
查找的时候 对于每个询问,如果子矩阵里的数已经超过了k个,说明答案在当前插入的这n个数里,倒着查找即可。
用链表维护询问,已经出解的直接跳过。
因为每个询问最多会查n次,所以复杂度O(nq),然而如果用树状数组代替前缀和维护的话是O(nqlog^2),妥妥T。
#include <stdio.h>
#include <algorithm>
#include <string.h>
using namespace std;
const int N=505;
struct arr{
int x,y,num;
bool operator <(const arr &A)const
{return num<A.num;}
}data[N*N];
struct typ{
int x1,x2,y1,y2,k;
}query[60005];
int n,m,t[N][N],c[N][N],x,i,j,k,p,s,ans[60005];
int pre[N*N],suf[N*N];
int x1,x2,y1,y2;
int main(){
scanf("%d%d",&n,&m);
for (i=1;i<=n;i++)
for (j=1;j<=n;j++){
scanf("%d",&x);
data[i*n-n+j].x=i;
data[i*n-n+j].y=j;
data[i*n-n+j].num=x;
}
sort(data+1,data+n*n+1);
for (i=1;i<=m;i++){
scanf("%d%d%d%d%d",&query[i].x1,&query[i].y1,&query[i].x2,&query[i].y2,&query[i].k);
suf[i]=i+1;
pre[i]=i-1;
}
suf[0]=1;
memset(t,0,sizeof t);
for (i=1;i<=n;i++){
int q=i*n-n+1,w=i*n;
for (j=q;j<=w;j++)
c[data[j].x][data[j].y]=1;
for (j=1;j<=n;j++)
for (k=1;k<=n;k++)
t[j][k]=c[j][k]+t[j-1][k]+t[j][k-1]-t[j-1][k-1];
for (j=suf[0];j<=m;j=suf[j]){
x1=query[j].x1;x2=query[j].x2;y1=query[j].y1;y2=query[j].y2;
s=t[x2][y2]+t[x1-1][y1-1]-t[x1-1][y2]-t[x2][y1-1];
if (s<query[j].k) continue;
for (p=w;p>=q;p--)
if (data[p].x>=x1 && data[p].x<=x2
&& data[p].y>=y1 && data[p].y<=y2)
if (s==query[j].k) {
ans[j]=data[p].num;
suf[pre[j]]=suf[j];
pre[suf[j]]=pre[j];
break;
}
else s--;
}
}
for (i=1;i<=m;i++)
printf("%d\n",ans[i]);
return 0;
}