BZOJ 2738 矩阵乘法 整体二分

该博客介绍了如何使用整体二分法解决一个涉及N*N矩阵的子矩形第K小数的查询问题。在二分过程中,将查询划分为两个集合,并对每个集合分别进行二分,直到找到所有询问的解。关键在于通过设置矩阵中小于等于x的元素为1,大于x的元素为0,统计小于等于x的元素数量来判断x是否可能是第k小的数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目大意:给出一个N*N的矩阵,每次询问一个子矩形的第K小数。

整体二分即在二分答案的同时把询问分到两个集合中再分别二分直到每个询问出解。
相当于把多个询问一起二分

如何判断一个数x是不是k小呢

把矩阵中的小于等于x的数设为1,大于x的数设为0,求询问矩阵中的小于等于x的数有多少个,大于等于k说明x 有可能是k小。

#include <cstdio>
#include <algorithm>
#define N 505
#define M 60005
#define INF 1000000000
using namespace std;
int n,m;
namespace BIT {
    inline int lowbit(int x) { return x&-x; }
    int c[N][N];
    int Sum(int x,int y) {
        int tmp=0;
        for(int i=x;i;i-=lowbit(i))
            for(int j=y;j;j-=lowbit(j))
                tmp+=c[i][j];
        return tmp;
    }
    void Modify(int x,int y,int v) {
        for(int i=x;i<=n;i+=lowbit(i))
            for(int j=y;j<=n;j+=lowbit(j))
                c[i][j]+=v;
        return ;
    }
}
using namespace BIT;
struct Data {
    int x,y,v;
    Data() {}
    Data(int _x,int _y,int _v):x(_x),y(_y),v(_v) {}
    bool operator < (const Data& rhs) const { return v<rhs.v; }
    void Update(int mode) { Modify(x,y,mode); }
}a[N*N];
struct Query {
    int ord,x1,y1,x2,y2,k;
    void scan(int _ord) {
        scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&k);
        ord=_ord;
        return ;
    }
    bool check() {
        return Sum(x1-1,y1-1)+Sum(x2,y2)-Sum(x1-1,y2)-Sum(x2,y1-1)>=k;
    }
}q[M],p[M];
int tot,now,ans[M];
void solve(int L,int R,int l,int r) {
    if(L>R || l>r) return ;
    int _L=L,_R=R,mid=l+r>>1;
    while(now<tot && a[now+1].v<=mid) a[++now].Update(1);
    while(now && a[now].v>mid) a[now--].Update(-1);
    for(int i=L;i<=R;++i) {
        if(q[i].check()) ans[q[i].ord]=mid, p[_L++]=q[i];
        else p[_R--]=q[i];
    }
    for(int i=L;i<=R;++i) q[i]=p[i];
    solve(L,_L-1,l,mid-1), solve(_R+1,R,mid+1,r);
    return ;
}
int main() {
    scanf("%d%d",&n,&m);
    for(int i=1,x;i<=n;++i)
        for(int j=1;j<=n;++j)
            scanf("%d",&x), a[++tot]=Data(i,j,x);
    sort(a+1,a+tot+1);
    for(int i=1;i<=m;++i) q[i].scan(i);
    solve(1,m,0,INF);
    for(int i=1;i<=m;++i) printf("%d\n",ans[i]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值