zoukankan      html  css  js  c++  java
  • [SDOI2010]粟粟的书架

    题目描述

    幸福幼儿园B29班的粟粟是一个聪明机灵、乖巧可爱的小朋友,她的爱好是画画和读书,尤其喜欢Thomas H. Cormen的文章。粟粟家中有一个R行C列的巨型书架,书架的每一个位置都摆有一本书,上数第i行、左数第j列摆放的书有Pi,j页厚。

    粟粟每天除了读书之外,还有一件必不可少的工作就是摘苹果,她每天必须摘取一个指定的苹果。粟粟家果树上的苹果有的高、有的低,但无论如何凭粟粟自己的个头都难以摘到。不过她发现,如果在脚下放上几本书,就可以够着苹果;她同时注意到,对于第i天指定的那个苹果,只要她脚下放置书的总页数之和不低于Hi,就一定能够摘到。

    由于书架内的书过多,父母担心粟粟一天内就把所有书看完而耽误了上幼儿园,于是每天只允许粟粟在一个特定区域内拿书。这个区域是一个矩形,第i天给定区域的左上角是上数第x1i行的左数第y1i本书,右下角是上数第x2i行的左数第y2i本书。换句话说,粟粟在这一天,只能在这﹙x2i-x1i+1﹚×﹙y2i-y1i+1﹚本书中挑选若干本垫在脚下,摘取苹果。

    粟粟每次取书时都能及时放回原位,并且她的书架不会再撤下书目或换上新书,摘苹果的任务会一直持续M天。给出每本书籍的页数和每天的区域限制及采摘要求,请你告诉粟粟,她每天至少拿取多少本书,就可以摘到当天指定的苹果。

    输入输出格式

    输入格式:

    输入文件susu.in第一行是三个正整数R, C, M。

    接下来是一个R行C列的矩阵,从上到下、从左向右依次给出了每本书的页数Pi,j。

    接下来M行,第i行给出正整数x1i, y1i, x2i, y2i, Hi,表示第i天的指定区域是﹙x1i, y1i﹚与﹙x2i, y2i﹚间的矩形,总页数之和要求不低于Hi。

    保证1≤x1i≤x2i≤R,1≤y1i≤y2i≤C。

    输出格式:

    输出文件susu.out有M行,第i行回答粟粟在第i天时为摘到苹果至少需要拿取多少本书。如果即使取走所有书都无法摘到苹果,则在该行输出“Poor QLW”(不含引号)。

    输入输出样例

    输入样例#1: 
    5 5 7
    14 15 9 26 53
    58 9 7 9 32
    38 46 26 43 38
    32 7 9 50 28
    8 41 9 7 17
    1 2 5 3 139
    3 1 5 5 399
    3 3 4 5 91
    4 1 4 1 33
    1 3 5 4 185
    3 3 4 3 23
    3 1 3 3 108
    输出样例#1:
    6
    15
    2
    Poor QLW
    9
    1
    3
    输入样例#2:
    1 10 7
    14 15 9 26 53 58 9 7 9 32
    1 2 1 9 170
    1 2 1 9 171
    1 5 1 7 115
    1 1 1 10 228
    1 4 1 4 45704571
    1 1 1 1 1
    1 7 1 8 16
    输出样例#2: 
    6
    7
    3
    10
    Poor QLW
    1
    2

    说明

    【数据规模和约定】

    对于10%的数据,满足R, C≤10;

    对于20%的数据,满足R, C≤40;

    对于50%的数据,满足R, C≤200,M≤200,000;

    另有50%的数据,满足R=1,C≤500,000,M≤20,000;

    对于100%的数据,满足1≤Pi,j≤1,000,1≤Hi≤2,000,000,000。

    题解:

    吐槽一下山东省,就不能把这道题分开吗,一道二合一的题目,而且两半的做法完全不同。(无语)

    1.对于第一问,由于n<=200,我们定义

      sum[i][j][k]表示坐标范围在(1,1)~(i,j)的矩阵中小于等于k的数有多少个

      value[i][j][k]表示坐标范围在(1,1)~(i,j)的矩阵中小于等于k的数和是多少

    根据题目要求,个数要尽量的小,那么数的值要尽量的大。

    考虑二分一个最小值,就可以通过预处理出的数组差分出答案了。

    2.对于第二问,原本的矩阵变成了一条链,类比一下第一问。

    如果忽略数据范围的话,我们还是可以预处理来两个数组sum[i][k],value[i][k]来表示前i位小于等于k的个数以及它们的和。

    显然这样做是不行的,因为n<=500000,所以必须要优化。

    考虑维护一个权值主席树,每一个数以及它之前的数构成一个状态,主席树中维护一个值域区间内的和以及个数。

    同样二分一个最小值,在主席树中查找区间中的和就可以了。

    细节:最后输出的答案应该在计算出来的答案的基础上尽量的减去二分出来的最小值,因为有的时候最小值只需要选一部分就可以了,而不需要将所有的最小值选上。

      1 //Never forget why you start
      2 #include<iostream>
      3 #include<cstdio>
      4 #include<cstdlib>
      5 #include<cstring>
      6 #include<cmath>
      7 #include<algorithm>
      8 #define ll(x) seg[x].l
      9 #define rr(x) seg[x].r
     10 using namespace std;
     11 int n,m,q;
     12 int map[205][205],value[205][205][1005],sum[205][205][1005];
     13 void work1(){
     14   int i,j,k;
     15   for(i=1;i<=n;i++)
     16     for(j=1;j<=m;j++){
     17       scanf("%d",&map[i][j]);
     18       sum[i][j][map[i][j]]++;
     19       value[i][j][map[i][j]]=map[i][j];
     20     }
     21   for(i=1;i<=n;i++)
     22     for(j=1;j<=m;j++)
     23       for(k=1000;k>=0;k--){
     24     sum[i][j][k]+=sum[i][j][k+1];
     25     value[i][j][k]+=value[i][j][k+1];
     26       }
     27   for(k=1000;k>=0;k--)
     28     for(i=1;i<=n;i++)
     29       for(j=1;j<=m;j++){
     30     sum[i][j][k]+=sum[i-1][j][k]+sum[i][j-1][k]-sum[i-1][j-1][k];
     31     value[i][j][k]+=value[i-1][j][k]+value[i][j-1][k]-value[i-1][j-1][k];
     32       }
     33   for(i=1;i<=q;i++){
     34     int a,b,c,d,tot;
     35     scanf("%d%d%d%d%d",&a,&b,&c,&d,&tot);
     36     int l=0,r=1000,ans=-1;
     37     while(l<=r){
     38       int mid=(l+r)>>1;
     39       if(value[c][d][mid]-value[c][b-1][mid]-value[a-1][d][mid]+value[a-1][b-1][mid]>=tot)ans=mid,l=mid+1;
     40       else r=mid-1;
     41     }
     42     if(ans==-1)printf("Poor QLW
    ");
     43     else printf("%d
    ",       sum[c][d][ans]-  sum[c][b-1][ans]-  sum[a-1][d][ans]+  sum[a-1][b-1][ans]-
     44                    (value[c][d][ans]-value[c][b-1][ans]-value[a-1][d][ans]+value[a-1][b-1][ans]-tot)/ans);
     45   }
     46 }
     47 int root[500005],cnt;
     48 struct seg{
     49   int l,r,sum,cnt;
     50 }seg[8000005];
     51 int newnode(int root){
     52   cnt++;
     53   seg[cnt].l=seg[root].l;
     54   seg[cnt].r=seg[root].r;
     55   seg[cnt].sum=seg[root].sum;
     56   seg[cnt].cnt=seg[root].cnt;
     57   return cnt;
     58 }
     59 void push_up(int root){
     60   seg[root].sum=seg[ll(root)].sum+seg[rr(root)].sum;
     61   seg[root].cnt=seg[ll(root)].cnt+seg[rr(root)].cnt;
     62 }
     63 void insert(int &root,int l,int r,int x){
     64   root=newnode(root);
     65   if(l==r){seg[root].sum+=l;seg[root].cnt++;return;}
     66   int mid=(l+r)>>1;
     67   if(x<=mid)insert(ll(root),l,mid,x);
     68   if(mid<x)insert(rr(root),mid+1,r,x);
     69   push_up(root);
     70 }
     71 int query(int lroot,int rroot,int left,int right,int l,int r){
     72   if(l<=left&&right<=r)return seg[rroot].sum-seg[lroot].sum;
     73   if(l>right||r<left)return 0;
     74   int mid=(left+right)>>1,ans=0;
     75   if(l<=mid)ans+=query(ll(lroot),ll(rroot),left,mid,l,r);
     76   if(mid<r)ans+=query(rr(lroot),rr(rroot),mid+1,right,l,r);
     77   return ans;
     78 }
     79 int query2(int lroot,int rroot,int left,int right,int l,int r){
     80   if(l<=left&&right<=r)return seg[rroot].cnt-seg[lroot].cnt;
     81   if(l>right||r<left)return 0;
     82   int mid=(left+right)>>1,ans=0;
     83   if(l<=mid)ans+=query2(ll(lroot),ll(rroot),left,mid,l,r);
     84   if(mid<r)ans+=query2(rr(lroot),rr(rroot),mid+1,right,l,r);
     85   return ans;
     86 }
     87 void work2(){
     88   int i,j;
     89   swap(n,m);
     90   for(i=1;i<=n;i++){
     91     int a;
     92     scanf("%d",&a);
     93     root[i]=root[i-1];
     94     insert(root[i],1,1000,a);
     95   }
     96   for(i=1;i<=q;i++){
     97     int a,b,c,d,tot;
     98     scanf("%d%d%d%d%d",&a,&b,&c,&d,&tot);
     99     int l=0,r=1000,ans=-1;
    100     while(l<=r){
    101       int mid=(l+r)>>1;
    102       if(query(root[b-1],root[d],1,1000,mid,1000)>=tot)ans=mid,l=mid+1;
    103       else r=mid-1;
    104     }
    105     if(ans==-1)printf("Poor QLW
    ");
    106     else printf("%d
    ",query2(root[b-1],root[d],1,1000,ans,1000)-(query(root[b-1],root[d],1,1000,ans,1000)-tot)/ans);
    107   }
    108 }
    109 int main(){
    110   int i,j;
    111   scanf("%d%d%d",&n,&m,&q);
    112   if(n==1)work2();
    113   else work1();
    114   return 0;
    115 }
  • 相关阅读:
    关键字 final
    继承中的构造方法
    方法的重写
    使用tar 和 split 将文件打包、压缩并分割成指定大小
    标准Web系统的架构分层
    Android的安全机制 1 -- 老罗
    Android 在 SElinux下 如何获得对一个内核节点的访问权限
    移动数据 流程分析
    ARM Linux 3.x的设备树(Device Tree)
    如何分析Android的Log
  • 原文地址:https://www.cnblogs.com/huangdalaofighting/p/8260768.html
Copyright © 2011-2022 走看看