BZOJ2738:矩阵乘法——题解

http://www.lydsy.com/JudgeOnline/problem.php?id=2738

Description

给你一个N*N的矩阵,不用算矩阵乘法,但是每次询问一个子矩形的第K小数。

Input

第一行两个数N,Q,表示矩阵大小和询问组数;
接下来N行N列一共N*N个数,表示这个矩阵;
再接下来Q行每行5个数描述一个询问:x1,y1,x2,y2,k表示找到以(x1,y1)为左上角、以(x2,y2)为右下角的子矩形中的第K小数。

Output

对于每组询问输出第K小的数。

Sample Input

2 2
2 1
3 4
1 2 1 2 1
1 1 2 2 3

Sample Output

1
3

整体二分二维版。

只需要把树状数组变成二维即可。

完后我们就对所有可行的取值二分然后就是整体二分的板子了。

(当然你也可以离散化)

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cctype>
#include<cstdio>
#include<vector>
#include<cmath>
using namespace std;
const int N=510;
const int Q=6e4+5;
inline int read(){
    int X=0,w=0;char ch=0;
    while(!isdigit(ch)){w|=ch=='-';ch=getchar();}
    while(isdigit(ch))X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
    return w?-X:X;
}
struct square{
    int val,x,y;
}v[N*N];
bool cmp(square a,square b){
    return a.val<b.val;
}
struct question{
    int lx,rx,ly,ry,k,id;
}q[Q],tmp1[Q],tmp2[Q];
int ans[Q],tr[N][N];
int n,m,cnt;
inline int lowbit(int t){return t&(-t);}
inline 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))
            tr[i][j]+=z;
}
inline int qry(int x,int y){
    int res=0;
    for(int i=x;i;i-=lowbit(i))
        for(int j=y;j;j-=lowbit(j))
            res+=tr[i][j];
    return res;
}
void solve(int L,int R,int l,int r){
    if(L>R)return;
    if(l==r){
        for(int i=L;i<=R;i++)ans[q[i].id]=v[l].val;
        return;
    }
    int idx1=0,idx2=0,mid=(l+r)>>1;
    for(int i=l;i<=mid;i++)add(v[i].x,v[i].y,1);
    for(int i=L;i<=R;i++){
        int sum=qry(q[i].rx,q[i].ry)+
                qry(q[i].lx-1,q[i].ly-1)-
                qry(q[i].rx,q[i].ly-1)-
                qry(q[i].lx-1,q[i].ry);
        if(sum>=q[i].k)tmp1[idx1++]=q[i];
        else q[i].k-=sum,tmp2[idx2++]=q[i];
    }
    for(int i=l;i<=mid;i++)add(v[i].x,v[i].y,-1);
    int MID=L+idx1;
    for(int i=L;i<MID;i++)q[i]=tmp1[i-L];
    for(int i=MID;i<=R;i++)q[i]=tmp2[i-MID];
    solve(L,MID-1,l,mid);solve(MID,R,mid+1,r);
    return;
}
int main(){
    n=read(),m=read();
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            v[++cnt].val=read();
            v[cnt].x=i,v[cnt].y=j;
        }
    }
    sort(v+1,v+cnt+1,cmp);
    for(int i=1;i<=m;i++){
        q[i].lx=read();q[i].ly=read();
        q[i].rx=read();q[i].ry=read();
        q[i].k=read();q[i].id=i;
    }
    solve(1,m,1,cnt);
    for(int i=1;i<=m;i++)printf("%d\n",ans[i]);
    return 0;
}

 

+++++++++++++++++++++++++++++++++++++++++++

+本文作者:luyouqi233。               +

+欢迎访问我的博客:http://www.cnblogs.com/luyouqi233/+

+++++++++++++++++++++++++++++++++++++++++++

转载于:https://www.cnblogs.com/luyouqi233/p/8387178.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值