Luogu1527[国家集训队]矩阵乘法

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\n",mat[ans[i]].v);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/pkh68/p/10526726.html