题解 P2468 【[SDOI2010]粟粟的书架】

· · 题解

应该算是一道二合一的题吧

观察数据范围

前50%的R,C均小于等于200,求给定矩阵中至少要取几个数加起来可以大于给定的值

可以搞一搞前缀和,二分最小值,变成一个判定性问题

value[i][j][k] 从(1,1)到(i,j)的矩阵中数值>=k的数的总和

num[i][j][k] 从(1,1)到(i,j)的矩阵中数值>=k的数的个数

后50%的R=1,C<=5*10^5,求给定序列中至少要取几个数加起来可以大于给定的值

依旧是二分最小值,可以用主席树维护

AC代码

#include<cstdio>
#include<iostream>
using namespace std;
#define re register
#define no printf("Poor QLW\n")
int n,m,t; // n hang m lie t tian
int a1,b1,a2,b2,h;
int page[202][202];
int value[202][202][1002];
int num[202][202][1002];
inline int get_value(int a1,int b1,int a2,int b2,int k){
    return value[a2][b2][k]-value[a1-1][b2][k]+value[a1-1][b1-1][k]-value[a2][b1-1][k];
}
inline int get_num(int a1,int b1,int a2,int b2,int k){
    return num[a2][b2][k]-num[a1-1][b2][k]+num[a1-1][b1-1][k]-num[a2][b1-1][k];
}
inline void work1(){
    re int maxn=0;
    for(re int i=1;i<=n;++i)
        for(re int j=1;j<=m;++j){
            scanf("%d",&page[i][j]);
            if(page[i][j]>maxn) maxn=page[i][j];
        }
    for(re int k=0;k<=maxn;++k) // 前缀和,容斥原理
        for(re int i=1;i<=n;++i)
            for(re int j=1;j<=m;++j){
                value[i][j][k]=value[i-1][j][k]+value[i][j-1][k]-value[i-1][j-1][k]+(page[i][j]>=k?page[i][j]:0);
                num[i][j][k]=num[i-1][j][k]+num[i][j-1][k]-num[i-1][j-1][k]+(page[i][j]>=k?1:0);
                // value[i][j][k] 从(1,1)到(i,j)的矩阵中数值>=k的数的总和
                // num[i][j][k] 从(1,1)到(i,j)的矩阵中数值>=k的数的个数
        }
    while(t--){
        scanf("%d%d%d%d%d",&a1,&b1,&a2,&b2,&h);
        if(get_value(a1,b1,a2,b2,0)<h) {no;continue;}
        re int l=0,r=maxn+1,ans=-1;
        while(l+1<r){
            re int mid=l+r>>1;
            if(get_value(a1,b1,a2,b2,mid)>=h) l=mid,ans=mid;
            else r=mid;
        }
        if(ans==-1) no;
        else printf("%d\n",get_num(a1,b1,a2,b2,ans)-(get_value(a1,b1,a2,b2,ans)-h)/ans);
    }    
}
#define N 5500002
int L[N],R[N],size[N],sum[N],root[N],cnt;
inline void update(int A,int &B,int l,int r,int x){
    B=++cnt;
    size[B]=size[A]+1;
    sum[B]=sum[A]+x;
    re int mid=l+r>>1;
    if(l==r) return;
    if(x<=mid) update(L[A],L[B],l,mid,x),R[B]=R[A];
    else update(R[A],R[B],mid+1,r,x),L[B]=L[A];        
}
inline int query(int A,int B,int l,int r,int k){
    re int ans=0;
    while(l<r){
        re int mid=l+r>>1;
        re int lch=sum[R[B]]-sum[R[A]];
        if(lch<k) ans+=size[R[B]]-size[R[A]],k-=lch,r=mid,B=L[B],A=L[A];
        else l=mid+1,B=R[B],A=R[A];
    }
    ans+=(k+l-1)/l;
    return ans;
}
inline void work2(){
    for(re int i=1;i<=m;++i){
        re int a; scanf("%d",&a);
        update(root[i-1],root[i],1,1000,a);
    }
    while(t--){
        scanf("%d%d%d%d%d",&a1,&b1,&a2,&b2,&h);
        if(sum[root[b2]]-sum[root[b1-1]]<h) {no;continue;}
        printf("%d\n",query(root[b1-1],root[b2],1,1000,h));
    }    
}
inline int dy(){
    scanf("%d%d%d",&n,&m,&t);
    if(n==1) work2();
    else work1();
    return 0;
}
int QAQ = dy();
int main(){;}