Luogu1527[国家集训队]矩阵乘法
题面:洛谷
解析
排名第(k)大,不妨考虑整体二分,二分第(k)大的数值(mid),,将大于(mid)的值视作1,添加到树状数组中,对每一个询问,判断它的询问区域中1的点的个数是否大于询问排名,即可将询问分作两部分递归,然而这样的复杂度是(O(n^{2}log{n})),我们不妨将二分值域变为二分数值在矩阵中对应的排名,再先将矩阵中的数排序,就不用每一次都遍历整个矩阵找到大于(mid)的数了。
代码
// luogu-judger-enable-o2
#include<cstdio>
#include<algorithm>
#define N 505
#define M 60005
using namespace std;
int n,m,id[M],ans[M],t1[M],t2[M],tc;
struct Q{ int x1,y1,x2,y2,k; }q[M];
struct Mat{
int v,x,y;
bool operator < (const Mat& rhs) const { return v<rhs.v; }
}mat[N*N];
int a[N][N];
inline int In(){
char c=getchar(); int x=0,ft=1;
for(;c<'0'||c>'9';c=getchar()) if(c=='-') ft=-1;
for(;c>='0'&&c<='9';c=getchar()) x=x*10+c-'0';
return x*ft;
}
inline int LB(int x){
return x&(-x);
}
inline void Add(int x,int y,int C){
for(int i=x;i<=n;i+=LB(i))
for(int j=y;j<=n;j+=LB(j))
a[i][j]+=C;
}
inline int Sum(int x,int y){
int s=0;
for(int i=x;i;i-=LB(i))
for(int j=y;j;j-=LB(j))
s+=a[i][j];
return s;
}
inline int Query(int x1,int y1,int x2,int y2){
return Sum(x2,y2)-Sum(x1-1,y2)-Sum(x2,y1-1)+Sum(x1-1,y1-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) ans[id[i]]=l;
return;
}
int mid=(l+r)>>1,p1=0,p2=0;
for(int i=l;i<=mid;++i) Add(mat[i].x,mat[i].y,1);
for(int i=ql,u,s;i<=qr;++i){
u=id[i]; s=Query(q[u].x1,q[u].y1,q[u].x2,q[u].y2);
if(s>=q[u].k) t1[++p1]=u;
else q[u].k-=s,t2[++p2]=u;
}
for(int i=l;i<=mid;++i) Add(mat[i].x,mat[i].y,-1);
for(int i=ql;i<=ql+p1-1;++i) id[i]=t1[i-ql+1];
for(int i=ql+p1;i<=qr;++i) id[i]=t2[i-ql-p1+1];
Solve(l,mid,ql,ql+p1-1); Solve(mid+1,r,ql+p1,qr);
}
int main(){
n=In(); m=In();
for(int i=1;i<=n;++i) for(int j=1;j<=n;++j)
mat[++tc].v=In(),mat[tc].x=i,mat[tc].y=j;
sort(mat+1,mat+1+tc);
for(int i=1;i<=m;++i){
q[i].x1=In(); q[i].y1=In();
q[i].x2=In(); q[i].y2=In();
q[i].k=In(); id[i]=i;
}
Solve(1,n*n,1,m);
for(int i=1;i<=m;++i) printf("%d
",mat[ans[i]].v);
return 0;
}