bzoj1926: [Sdoi2010]粟粟的书架

传送门
对于第一问(R,C<=200);
预处理f[x][y][k],s[x][y][k].表示从(1,1)到(x,y)中大于等于k的数的和与大于等于k的数的个数。
然后二分最小的数即可。
对于第二问(R=1):
我们还是二分最小数。
判断就变成了询问一段区间内大于等于x的数的和以及它们的个数。
显然主席树可以处理这个

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<queue>
#define N 11000000
#define M 210 
using namespace std;
int n,m,T,a[M][M][M*5],b[M][M][M*5],c[M][M],x1,y1,x2,y2,h;
int t,rt[550000],ls[N],rs[N],v[N],s[N],cnt;
void work1(){
    for (int i=1;i<=n;i++)
        for (int j=1;j<=m;j++) scanf("%d",&c[i][j]);
    for (int i=1;i<=n;i++)
        for (int j=1;j<=m;j++)
            for (int k=1;k<=1000;k++){
                a[i][j][k]=a[i][j-1][k]+a[i-1][j][k]-a[i-1][j-1][k];
                b[i][j][k]=b[i][j-1][k]+b[i-1][j][k]-b[i-1][j-1][k];
                if (c[i][j]>=k) a[i][j][k]++,b[i][j][k]+=c[i][j];
            }
    while (T--){
        scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&h);
        int l=0,r=1001; x1--; y1--;
        while (l+1<r){
            int mid=(l+r)/2;
            if (b[x1][y1][mid]+b[x2][y2][mid]-b[x1][y2][mid]-b[x2][y1][mid]>=h)
                l=mid; else r=mid;
        }
        if (l==0) puts("Poor QLW");
        else{
            int sum1=b[x1][y1][l]+b[x2][y2][l]-b[x2][y1][l]-b[x1][y2][l]-h;
            int sum2=a[x1][y1][l]+a[x2][y2][l]-a[x2][y1][l]-a[x1][y2][l];
            printf("%d\n",sum2-sum1/l);
        }
    }
}
void add(int l,int r,int x,int &y,int k){
    y=++cnt; v[y]=v[x]+1; s[y]=s[x]+k;
    if (l==r) return;
    int mid=(l+r)/2;
    if (k<=mid) rs[y]=rs[x],add(l,mid,ls[x],ls[y],k);
    else ls[y]=ls[x],add(mid+1,r,rs[x],rs[y],k);
}
int ask(int x,int y,int k){
    int l=1,r=1000,ok=0;
    x=rt[x-1]; y=rt[y];
    if (s[y]-s[x]<h) return -1;
    while (l<r){
        int mid=(l+r)/2,t=s[rs[y]]-s[rs[x]];
        if (t<k){
            ok+=v[rs[y]]-v[rs[x]]; k-=t; r=mid;
            x=ls[x]; y=ls[y];
        }
        else{
            l=mid+1; x=rs[x]; y=rs[y];
        }
    }
    ok+=(k+l-1)/l;
    return ok;
}
void work2(){
    for (int i=1,x;i<=m;i++){
        scanf("%d",&x);
        add(1,1000,rt[i-1],rt[i],x);
    }
    while (T--){
        scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&h);
        int ans=ask(y1,y2,h);
        if (ans==-1) puts("Poor QLW");
        else printf("%d\n",ans);
    }
}
int main(){
    scanf("%d%d%d",&n,&m,&T);
    if (n!=1) work1(); else work2();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值