洛谷P2468 粟粟的书架

题目链接:https://www.luogu.org/problemnew/show/P2468

知识点:  可持久化线段树、二分、前缀和

解题思路:

  对于 \(R, C \le 200, M \le 200,000\) 的数据,先处理出前缀和,然后二分取出的数中最小的数。细节请参考 \(solve2()\) 函数。

  对于 \(R=1,C \le 500,000,M \le 20,000\) 的数据,维护一棵记录 \([1,1000]\) 的数在各个历史版本的个数和相应的总和的可持久化线段树,将一行上各列的数依序更新进去。细节请参考 \(solve2()\) 及相关函数。

AC代码:

  1 #include <bits/stdc++.h>
  2 
  3 using namespace std;
  4 typedef long long LL;
  5 const int inf=0x3f3f3f3f;
  6 const int maxc=500003;
  7 
  8 int sum[maxc*15];//记录总和
  9 int has[maxc*15];//记录出现次数
 10 int lson[maxc*15],rson[maxc*15],T[maxc*15];
 11 int tot;
 12 int p1[maxc];
 13 void build(int l,int r,int &rt){
 14     rt=++tot;
 15     sum[rt]=has[rt]=0;
 16     if(l==r)    return;
 17     int m=(l+r)>>1;
 18     build(l,m,lson[rt]);
 19     build(m+1,r,rson[rt]);
 20 }
 21 void update(int last,int p,int l,int r,int &rt){
 22     rt=++tot;
 23     lson[rt] = lson[last];
 24     rson[rt] = rson[last];
 25     has[rt] = has[last] + 1;
 26     sum[rt] = sum[last] + p;
 27     if (l == r)    return;
 28     int m = (l + r) >> 1;
 29     if (p <= m)    update(lson[last], p, l, m, lson[rt]);
 30     else            update(rson[last], p, m + 1, r, rson[rt]);
 31 }
 32 //用一种类似二分的方式来查询
 33 int query(int s,int t,int h,int l,int r){
 34     int ret=0;
 35     while(l<r){
 36         int m=(l+r)>>1;
 37         int rch=sum[rson[t]]-sum[rson[s]];//优先用右子树(因为其上的数字较大)
 38         if(rch<h)//如果只用右子树仍然不够,则直接加上右子树的总和,再查询左子树
 39             ret+=has[rson[t]]-has[rson[s]],h-=rch,s=lson[s],t=lson[t],r=m;
 40         else//否则查询右子树
 41             s=rson[s],t=rson[t],l=m+1;
 42     }
 43     ret+=(h-1)/l+1;//答案修正
 44     return ret;
 45 }
 46 void solve1(int R,int C,int M){
 47     build(1,1000,T[0]);
 48     for(int i=1;i<=C;i++){
 49         scanf("%d",&p1[i]);
 50         update(T[i-1],p1[i],1,1000,T[i]);
 51     }
 52     int x1,y1,x2,y2,h;
 53     for(int i=0;i<M;i++){
 54         scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&h);
 55         if(sum[T[y2]]-sum[T[y1-1]]<h)   printf("Poor QLW\n");
 56         else
 57             printf("%d\n",query(T[y1-1],T[y2],h,1,1000));
 58     }
 59 }
 60 
 61 int p[203][203];
 62 int have[203][203][1003];//have[x][y][k]: 代表p矩阵中在点(x,y)左上角的子矩阵里大于等于k的数的个数
 63 int height[203][203][1003];//height[x][y][k]: 代表p矩阵中在点(x,y)左上角的子矩阵里大于等于k的数的总和
 64 int get_have(int x1,int y1,int x2,int y2,int ind){
 65     return have[x2][y2][ind]-have[x1][y2][ind]-have[x2][y1][ind]+have[x1][y1][ind];
 66 }
 67 int get_height(int x1,int y1,int x2,int y2,int ind){
 68     return height[x2][y2][ind]-height[x1][y2][ind]-height[x2][y1][ind]+height[x1][y1][ind];
 69 }
 70 void solve2(int R,int C,int M){
 71     for(int i=1;i<=R;i++){
 72         for(int j=1;j<=C;j++)
 73             scanf("%d",&p[i][j]);
 74     }
 75     for(int i=1000;i>=0;i--){
 76         for(int x=1;x<=R;x++){
 77             for(int y=1;y<=C;y++){
 78                 //转移
 79                 height[x][y][i]=height[x-1][y][i]+height[x][y-1][i]-height[x-1][y-1][i]+(p[x][y]>=i?p[x][y]:0);
 80                 have[x][y][i]=have[x-1][y][i]+have[x][y-1][i]-have[x-1][y-1][i]+(p[x][y]>=i?1:0);
 81             }
 82         }
 83     }
 84     int x1,y1,x2,y2;
 85     int h;
 86     while(M--){
 87         scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&h);
 88         int l=0,r=1001;//二分取出来的数中最小的数,此处 r 如果没有初始化为 1001 的话会 WA 一个点
 89         int ans=-1;
 90         for(int i=0;i<100;i++){
 91             int m=(l+r)>>1;
 92             int tmp=get_height(x1-1,y1-1,x2,y2,m);
 93             if(tmp>=h)
 94                 l=m,ans=get_have(x1-1,y1-1,x2,y2,m)-(tmp-h)/m;//减掉多余的部分
 95             else
 96                 r=m;
 97         }
 98         if(ans==-1) printf("Poor QLW\n");
 99         else    printf("%d\n",ans);
100     }
101 }
102 
103 int main(){
104 //    freopen("in.txt","r",stdin);
105     int R,C,M;
106     scanf("%d%d%d",&R,&C,&M);
107     if(R==1)    solve1(R,C,M);
108     else    solve2(R,C,M);
109     return 0;
110 }

 

posted @ 2018-05-23 00:12  Blogggggg  阅读(235)  评论(0编辑  收藏  举报