题目链接

https://www.lydsy.com/JudgeOnline/problem.php?id=1926

思路

数据范围:50%这样这样……另50%那样那样……
怎么这么毒瘤啊!
还要分开写前50%的数据和后50%的数据的解法!

这道题首先可以观察到:选书肯定是选择厚度尽量大的书(贪心)

对于前50%的数据:
vali,j,k表示(1,1)(i,j)这个矩形中厚度>=k的书的厚度总和。
sumi,j,k表示(1,1)(i,j)这个矩形中厚度>=k的书的个数。
上面两个状态很好转移。
观察到答案具有可二分性,二分选择的书中最小的厚度,注意最后有厚度相同的情况要特殊处理。

对于后50%的数据:
实际上就是直线上给定区间中,满足val>=ksum的最小值(沿用上面的定义)。
显然可以用主席树维护这个值,注意最后相同的情况同样要预处理。

代码

#include <cstdio>

int r,c,m;

namespace dp
{
  const int maxn=200;
  const int maxv=1000;

  int val[maxn+10][maxn+10][maxv+10],num[maxn+10][maxn+10][maxv+10],p[maxn+10][maxn+10];

  inline int getval(int l,int d,int r,int u,int k)
  {
    return val[r][u][k]-val[l-1][u][k]-val[r][d-1][k]+val[l-1][d-1][k];
  }

  inline int getnum(int l,int d,int r,int u,int k)
  {
    return num[r][u][k]-num[l-1][u][k]-num[r][d-1][k]+num[l-1][d-1][k];
  }

  int solve()
  {
    for(register int i=1; i<=r; ++i)
      {
        for(register int j=1; j<=c; ++j)
          {
            scanf("%d",&p[i][j]);
          }
      }
    for(register int k=1; k<=maxv; ++k)
      {
        for(register int i=1; i<=r; ++i)
          {
            for(register int j=1; j<=c; ++j)
              {
                val[i][j][k]=val[i-1][j][k]+val[i][j-1][k]-val[i-1][j-1][k]+((p[i][j]>=k)?p[i][j]:0);
                num[i][j][k]=num[i-1][j][k]+num[i][j-1][k]-num[i-1][j-1][k]+((p[i][j]>=k)?1:0);
              }
          }
      }
    while(m--)
      {
        int a,b,c,d,e,l=1,r=maxv,ans=-1;
        scanf("%d%d%d%d%d",&a,&b,&c,&d,&e);
        while(l<=r)
          {
            int mid=(l+r)>>1;
            if(getval(a,b,c,d,mid)<e)
              {
                r=mid-1;
              }
            else
              {
                l=mid+1;
                ans=mid;
              }
          }
        if(ans==-1)
          {
            puts("Poor QLW");
          }
        else
          {
            printf("%d\n",getnum(a,b,c,d,ans)-(getval(a,b,c,d,ans)-e)/ans);
          }
      }
    return 0;
  }
}

namespace pst
{
  const int maxn=500000;
  const int maxv=1000;

  struct node
  {
    node* son[2];
    int val,num;
  };

  node* root[maxn+10];
  int p[maxn+10];

  inline int updata(node* now)
  {
    now->val=now->son[0]->val+now->son[1]->val;
    now->num=now->son[0]->num+now->son[1]->num;
    return 0;
  }

  int build(node* now,int l,int r)
  {
    if(l==r)
      {
        now->val=now->num=0;
        return 0;
      }
    int mid=(l+r)>>1;
    build(now->son[0]=new node(),l,mid);
    build(now->son[1]=new node(),mid+1,r);
    updata(now);
    return 0;
  }

  node* add(node* now,int l,int r,int pos)
  {
    node* a=new node();
    if(l==r)
      {
        a->num=now->num+1;
        a->val=now->val+pos;
        return a;
      }
    int mid=(l+r)>>1;
    if(pos<=mid)
      {
        a->son[0]=add(now->son[0],l,mid,pos);
        a->son[1]=now->son[1];
      }
    else
      {
        a->son[0]=now->son[0];
        a->son[1]=add(now->son[1],mid+1,r,pos);
      }
    updata(a);
    return a;
  }

  int query(node* a,node* b,int l,int r,int k)
  {
    if(l==r)
      {
        return (k+l-1)/l;
      }
    int cnt=b->son[1]->val-a->son[1]->val,mid=(l+r)>>1;
    if(k<=cnt)
      {
        return query(a->son[1],b->son[1],mid+1,r,k);
      }
    else
      {
        return query(a->son[0],b->son[0],l,mid,k-cnt)+b->son[1]->num-a->son[1]->num;
      }
  }

  int solve()
  {
    for(register int i=1; i<=c; ++i)
      {
        scanf("%d",&p[i]);
      }
    root[0]=new node();
    build(root[0],1,maxv);
    for(register int i=1; i<=c; ++i)
      {
        root[i]=add(root[i-1],1,maxv,p[i]);
      }
    while(m--)
      {
        int a,b,c,d,e;
        scanf("%d%d%d%d%d",&a,&b,&c,&d,&e);
        if(root[d]->val-root[b-1]->val<e)
          {
            puts("Poor QLW");
          }
        else
          {
            printf("%d\n",query(root[b-1],root[d],1,maxv,e));
          }
      }
    return 0;
  }
}

int main()
{
  scanf("%d%d%d",&r,&c,&m);
  if(r==1)
    {
      pst::solve();
    }
  else
    {
      dp::solve();
    }
  return 0;
}