正题
题目链接:https://www.luogu.org/problem/P1527
题目大意
给出一个矩阵,每个询问求子矩阵中的第kkk小数。
解题思路
我们发现我们对于每个询问我们可以二分答案,然后查找该子矩阵中有多少个数≤mid\leq mid≤mid来判断。
但是这样时间复杂度和空间复杂度显然无法接受。
所以我们考虑整体二分,我们将所有的询问放在一起二分,然后单独处理左边的,再处理右边的。其中我们可以用二维树状数组可以在O(mid+qlog2n)O(mid+q\log^2 n)O(mid+qlog2n)的时间内处理子矩阵中有多少个数≤mid\leq mid≤mid
codecodecode
// luogu-judger-enable-o2
#include<cstdio>
#include<cstring>
#include<algorithm>
#define lowbit(x) (x&-x)
using namespace std;
const int N=510,M=61000;
int n,m,t[N][N],cnt,ans[M],last;
struct node{int x,y,w;
}a[N*N];
struct Que_node{int x1,y1,x2,y2,k,pos;
}q[M],q1[M],q2[M];
void change(int x,int y,int val)
{for(int i=x;i<=n;i+=lowbit(i))for(int j=y;j<=n;j+=lowbit(j))t[i][j]+=val;return;
}
int ask(int x,int y)
{int ans=0;for(int i=x;i;i-=lowbit(i))for(int j=y;j;j-=lowbit(j))ans+=t[i][j];return ans;
}
int query(int x1,int y1,int x2,int y2)
{return ask(x2,y2)+ask(x1-1,y1-1)-ask(x1-1,y2)-ask(x2,y1-1);}
void get_ans(int l,int r,int L,int R)
{int mid=(l+r)>>1;if(L>R) return;if(l==r){for(int i=L;i<=R;i++)ans[q[i].pos]=a[l].w;return;}for(int i=l;i<=mid;i++)change(a[i].x,a[i].y,1);int cnt1=0,cnt2=0;for(int i=L;i<=R;i++){int x=query(q[i].x1,q[i].y1,q[i].x2,q[i].y2);if(x>=q[i].k) q1[++cnt1]=q[i];else q[i].k-=x,q2[++cnt2]=q[i];}for(int i=l;i<=mid;i++)change(a[i].x,a[i].y,-1);int z=L-1;for(int i=1;i<=cnt1;i++)q[++z]=q1[i];for(int i=1;i<=cnt2;i++)q[++z]=q2[i];get_ans(l,mid,L,L+cnt1-1);get_ans(mid+1,r,L+cnt1,R);return;
}
bool cMp(node x,node y)
{return x.w<y.w;}
int main()
{scanf("%d%d",&n,&m);for(int i=1;i<=n;i++)for(int j=1;j<=n;j++){int x;scanf("%d",&x);a[++cnt]=(node){i,j,x};}sort(a+1,a+1+cnt,cMp);for(int i=1;i<=m;i++)scanf("%d%d%d%d%d",&q[i].x1,&q[i].y1,&q[i].x2,&q[i].y2,&q[i].k),q[i].pos=i;get_ans(1,cnt,1,m);for(int i=1;i<=m;i++)printf("%d\n",ans[i]);
}
/*
3 3
2 0 6
3 4 0
8 9 5
1 2 2 3 1
1 1 3 3 7
*/