版权声明:2333 https://blog.csdn.net/liangzihao1/article/details/81915669
题目描述
给你一个N*N的矩阵,不用算矩阵乘法,但是每次询问一个子矩形的第K小数。
输入输出格式
输入格式:
第一行两个数N,Q,表示矩阵大小和询问组数;
接下来N行N列一共N*N个数,表示这个矩阵;
再接下来Q行每行5个数描述一个询问:x1,y1,x2,y2,k表示找到以(x1,y1)为左上角、以(x2,y2)为右下角的子矩形中的第K小数。
输出格式:
对于每组询问输出第K小的数。
输入输出样例
输入样例#1:
2 2
2 1
3 4
1 2 1 2 1
1 1 2 2 3
输出样例#1:
1
3
说明
矩阵中数字是10^9以内的非负整数;
20%的数据:N<=100,Q<=1000;
40%的数据:N<=300,Q<=10000;
60%的数据:N<=400,Q<=30000;
100%的数据:N<=500,Q<=60000。
分析:
我们可以对每个询问二分答案,但是这样每次询问都要把前缀和跑出来,显然非常慢。
注意到当我们把前缀和跑出来时,可以对所有询问进行判断,然后把小于等于
的放在左边,大于
的放在右边,递归处理,这个很像
。
然后我就真的对值域二分……
其实我们可以先对矩阵内的位置排序,二分这个数组显然更优秀。每次进行操作后还原二维树状数组即可,到小区间后插入和删除操作会边少,区间
也能承受,这个也和
很像。
二维树状数组和一维差不多,一层循环变二层。复杂度是
,但是还是跑得很快的。
代码:
// luogu-judger-enable-o2
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <cmath>
const int maxn=507;
const int maxq=6e4+7;
const int maxd=1e9+7;
using namespace std;
int t[maxn][maxn];
int n,test,cnt,x;
struct rec{
int x,y,k;
}a[maxn*maxn];
struct node{
int a,b,c,d,k,num,ans;
}q[maxq],L[maxq],R[maxq];
bool cmp(node x,node y)
{
return x.num<y.num;
}
bool cmp1(rec x,rec y)
{
return x.k<y.k;
}
void ins(int x,int y,int d)
{
for (int i=x;i<=n;i+=i&(-i))
{
for (int j=y;j<=n;j+=j&(-j))
{
t[i][j]+=d;
}
}
}
int getsum(int x,int y)
{
int sum=0;
for (int i=x;i>0;i-=i&(-i))
{
for (int j=y;j>0;j-=j&(-j))
{
sum+=t[i][j];
}
}
return sum;
}
int count(int a,int b,int c,int d)
{
return getsum(c,d)-getsum(a-1,d)-getsum(c,b-1)+getsum(a-1,b-1);
}
void solve(int l,int r,int ql,int qr)
{
if (ql>qr) return;
if (l==r)
{
for (int i=ql;i<=qr;i++) q[i].ans=a[l].k;
return;
}
int mid=(l+r)/2;
for (int i=l;i<=mid;i++) ins(a[i].x,a[i].y,1);
int cnt1=0,cnt2=0;
for (int i=ql;i<=qr;i++)
{
int d=count(q[i].a,q[i].b,q[i].c,q[i].d);
if (q[i].k<=d) L[++cnt1]=q[i];
else q[i].k-=d,R[++cnt2]=q[i];
}
for (int i=l;i<=mid;i++) ins(a[i].x,a[i].y,-1);
for (int i=1;i<=cnt1;i++) q[ql+i-1]=L[i];
for (int i=1;i<=cnt2;i++) q[ql+cnt1-1+i]=R[i];
solve(l,mid,ql,ql+cnt1-1);
solve(mid+1,r,ql+cnt1,qr);
}
int main()
{
scanf("%d%d",&n,&test);
for (int i=1;i<=n;i++)
{
for (int j=1;j<=n;j++)
{
scanf("%d",&x);
a[++cnt]=(rec){i,j,x};
}
}
for (int i=1;i<=test;i++)
{
scanf("%d%d%d%d%d",&q[i].a,&q[i].b,&q[i].c,&q[i].d,&q[i].k);
q[i].num=i;
}
sort(a+1,a+cnt+1,cmp1);
solve(1,cnt,1,test);
sort(q+1,q+test+1,cmp);
for (int i=1;i<=test;i++) printf("%d\n",q[i].ans);
}