bzoj2738: 矩阵乘法【整体二分+二维树状数组】

10 篇文章 0 订阅
3 篇文章 0 订阅
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

HINT

  矩阵中数字是109以内的非负整数;

  20%的数据:N<=100,Q<=1000;

  40%的数据:N<=300,Q<=10000;

  60%的数据:N<=400,Q<=30000;

  100%的数据:N<=500,Q<=60000

解题思路:

整体二分裸题,直接把区间第k大中的一维树状数组改成二维即可,也可以用扫描线,要快一点。

#include<bits/stdc++.h>
using namespace std;

int getint()
{
    int i=0,f=1;char c;
    for(c=getchar();(c!='-')&&(c<'0'||c>'9');c=getchar());
    if(c=='-')f=-1,c=getchar();
    for(;c>='0'&&c<='9';c=getchar())i=(i<<3)+(i<<1)+c-'0';
    return i*f;
}

const int N=505,M=60005;
int n,m,cnt,bit[N][N],val[M],ans[M];
struct node{int x,y,val;}a[N*N];
inline bool cmp (const node &a,const node &b){return a.val<b.val;}
struct node1{int x1,y1,x2,y2,k,id;}q[M],t[M];

void insert(int x,int y,int v)
{
    for(int i=x;i<=n;i+=i&(-i))
        for(int j=y;j<=n;j+=j&(-j))
            bit[i][j]+=v;
}

int query(int x,int y)
{
    int res=0;
    for(int i=x;i;i-=i&(-i))
        for(int j=y;j;j-=j&(-j))
            res+=bit[i][j];
    return res;
}

void calc(int ql,int qr,int vl,int vr)
{
    int l=1,r=cnt;
    while(l<=r)
    {
        int mid=l+r>>1;
        if(a[mid].val>=vl)r=mid-1;
        else l=mid+1;
    }
    for(int i=ql;i<=qr;i++)val[i]=0;
    if(l>cnt)return;
    for(int i=l;a[i].val<=vr&&i<=cnt;i++)insert(a[i].x,a[i].y,1);
    for(int i=ql;i<=qr;i++)
        val[i]=query(q[i].x2,q[i].y2)+query(q[i].x1-1,q[i].y1-1)-query(q[i].x1-1,q[i].y2)-query(q[i].x2,q[i].y1-1);
    for(int i=l;a[i].val<=vr&&i<=cnt;i++)insert(a[i].x,a[i].y,-1);
}

void solve(int ql,int qr,int vl,int vr)
{
    if(ql>qr)return;
    if(vl==vr)
    {
        for(int i=ql;i<=qr;i++)ans[q[i].id]=vl;
        return;
    }
    int vmid=vl+vr>>1;
    calc(ql,qr,vl,vmid);
    int st=ql-1;
    for(int i=ql;i<=qr;i++)
        if(q[i].k<=val[i])t[++st]=q[i];
    int qmid=st;
    for(int i=ql;i<=qr;i++)
        if(q[i].k>val[i])q[i].k-=val[i],t[++st]=q[i];
    for(int i=ql;i<=qr;i++)q[i]=t[i];
    solve(ql,qmid,vl,vmid),solve(qmid+1,qr,vmid+1,vr);
}

int main()
{
    //freopen("lx.in","r",stdin);
    //freopen("lx.out","w",stdout);
    n=getint(),m=getint();
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            a[++cnt]=(node){i,j,getint()};
    sort(a+1,a+cnt+1,cmp);
    for(int i=1;i<=m;i++)
    {
        q[i].x1=getint(),q[i].y1=getint();
        q[i].x2=getint(),q[i].y2=getint();
        q[i].k=getint(),q[i].id=i;
    }
    solve(1,m,0,1e9);
    for(int i=1;i<=m;i++)printf("%d\n",ans[i]);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值