P1527 [国家集训队]矩阵乘法 题解
\(q\) 次查询矩形第 \(k\) 小。考虑使用整体二分,二分一个值 \(mid\),矩形中小于等于 \(mid\) 的数设为 \(1\),大于 \(mid\) 的数设为 \(0\),统计每个询问的矩形中的和,若大于等于 \(k\),表示答案应该在 \([L,mid]\) 范围内,放到左边继续递归,否则放到右边。
考虑一下整体二分的复杂度。整体二分需要处理的次数很多,但是可以按层考虑,每一层每一个询问或修改只会被操作一次,因为只会出现在一个区间里。所以总的复杂度就是 \(O(\)询问层数 \(\log n\times\) 操作一个询问或修改的复杂度 \()\),所以本题的时间复杂度为 \(O(n\log n\log v)\)。
这道题目还应当注意二维树状数组的写法和求矩形内数的和的时候的式子。
点击查看代码
#include<iostream>
#include<cstdio>
using namespace std;
inline int min(const int &a,const int &b){return a<b?a:b;}
inline int max(const int &a,const int &b){return a>b?a:b;}
const int N=4e5+13,M=500+13;
struct Node{int x,y,x2,y2,k,op;}q[N],lq[N],rq[N];
int n,m,tot,ans[N];
namespace BIT{
int t[M][M];
#define lowbit(x) (x&-x)
inline void add(int x,int y,int k){
for(int i=x;i<=n;i+=lowbit(i))
for(int j=y;j<=n;j+=lowbit(j))
t[i][j]+=k;
}
inline int sum(int x,int y){
int res=0;
for(int i=x;i;i-=lowbit(i))
for(int j=y;j;j-=lowbit(j))
res+=t[i][j];
return res;
}
}using BIT::add;using BIT::sum;
void solve(int L,int R,int l,int r){
if(l>r) return;
if(L==R){
for(int i=l;i<=r;++i)
if(q[i].op) ans[q[i].op]=L;
return;
}
int mid=(L+R)>>1,lt=0,rt=0;
for(int i=l;i<=r;++i){
if(!q[i].op){
if(q[i].k<=mid) add(q[i].x,q[i].y,1),lq[++lt]=q[i];
else rq[++rt]=q[i];
}
else{
int res=sum(q[i].x2,q[i].y2)-sum(q[i].x-1,q[i].y2)-sum(q[i].x2,q[i].y-1)+sum(q[i].x-1,q[i].y-1);
if(res>=q[i].k) lq[++lt]=q[i];
else q[i].k-=res,rq[++rt]=q[i];
}
}
for(int i=l;i<=r;++i)
if(!q[i].op&&q[i].k<=mid) add(q[i].x,q[i].y,-1);
for(int i=1;i<=lt;++i) q[l+i-1]=lq[i];
for(int i=1;i<=rt;++i) q[l+lt+i-1]=rq[i];
solve(L,mid,l,l+lt-1);
solve(mid+1,R,l+lt,r);
}
int main(){
int ll=1e9,rr=0;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i)
for(int j=1,x;j<=n;++j){
scanf("%d",&x);ll=min(ll,x),rr=max(rr,x);
q[++tot]=(Node){i,j,0,0,x,0};
}
for(int i=1;i<=m;++i){
++tot;
scanf("%d%d%d%d%d",&q[tot].x,&q[tot].y,&q[tot].x2,&q[tot].y2,&q[tot].k);
q[tot].op=i;
}
solve(ll,rr,1,tot);
for(int i=1;i<=m;++i) printf("%d\n",ans[i]);
return 0;
}