目录
显示
题目链接
https://www.luogu.com.cn/problem/P1527
题解
整体二分模板题。
整体二分,它的意思就是将所有询问放在一块进行二分,从而达到优化时间复杂度的目的。
我们刚开始将所有询问的答案都设置为 \(mid\),问题就变成了统计矩阵指定区域内小于等于 \(mid\) 的元素个数。
将小于等于 \(mid\) 的位置设置为 \(1\),其余设置为 \(0\),这样就可以很方便地用二维树状数组统计答案。
接下来将询问分为两部分,结果小于等于 \(mid\) 的询问放在左边,否则放在右边,递归即可。
(记得在递归前把树状数组清空)
#include <iostream>
#include <algorithm>
using namespace std;
struct node
{
int x,y,z;
bool operator<(const node&a)const
{
return z<a.z;
}
}a[250005];
struct query
{
int x1,y1,x2,y2,k;
}q[60005];
struct BIT
{
int a[505][505],n;
void init(int N)
{
n=N;
}
int lowbit(int x)
{
return x&(-x);
}
void add(int x,int y,int z)
{
for(int i=x;i<=n;i+=lowbit(i))
for(int j=y;j<=n;j+=lowbit(j))
a[i][j]+=z;
}
int query(int x,int y)
{
int ans=0;
for(int i=x;i;i-=lowbit(i))
for(int j=y;j;j-=lowbit(j))
ans+=a[i][j];
return ans;
}
int query_square(int x1,int y1,int x2,int y2)
{
return query(x2,y2)-query(x2,y1-1)-query(x1-1,y2)+query(x1-1,y1-1);
}
}bit;
int id[60005],ans[60005],cur[60005];
int q1[60005],q2[60005];
int n,Q;
void solve(int l,int r,int ql,int qr)
{
if(ql>qr)return;
if(l==r)
{
for(int i=ql;i<=qr;i++)
ans[id[i]]=a[l].z;
return;
}
int mid=(l+r)>>1;
for(int i=l;i<=mid;i++)
bit.add(a[i].x,a[i].y,1);
int cnt1=0,cnt2=0;
for(int i=ql;i<=qr;i++)
{
int u=id[i];
int res=cur[u]+bit.query_square(q[u].x1,q[u].y1,q[u].x2,q[u].y2);
if(res>=q[u].k)
q1[++cnt1]=u;
else
q2[++cnt2]=u,cur[u]=res;
}
int cnt3=ql-1;
for(int i=1;i<=cnt1;i++)
id[++cnt3]=q1[i];
for(int i=1;i<=cnt2;i++)
id[++cnt3]=q2[i];
for(int i=l;i<=mid;i++)
bit.add(a[i].x,a[i].y,-1);
solve(l,mid,ql,ql+cnt1-1);
solve(mid+1,r,ql+cnt1,qr);
}
int main()
{
ios::sync_with_stdio(false);
cin>>n>>Q;
bit.init(n);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
int id=(i-1)*n+j;
cin>>a[id].z;
a[id].x=i,a[id].y=j;
}
sort(a+1,a+n*n+1);
for(int i=1;i<=Q;i++)
{
cin>>q[i].x1>>q[i].y1>>q[i].x2>>q[i].y2>>q[i].k;
id[i]=i;
}
solve(1,n*n,1,Q);
for(int i=1;i<=Q;i++)
cout<<ans[i]<<endl;
return 0;
}