zoukankan      html  css  js  c++  java
  • 洛谷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
    ");
     56         else
     57             printf("%d
    ",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
    ");
     99         else    printf("%d
    ",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 }
    “这些年我一直提醒自己一件事情,千万不要自己感动自己。大部分人看似的努力,不过是愚蠢导致的。什么熬夜看书到天亮,连续几天只睡几小时,多久没放假了,如果这些东西也值得夸耀,那么富士康流水线上任何一个人都比你努力多了。人难免天生有自怜的情绪,唯有时刻保持清醒,才能看清真正的价值在哪里。”
  • 相关阅读:
    mac登录界面的背景壁纸图片位置
    【转载】MAC系统修改帐号短名和个人文件夹名称
    ios 6.x系统UITextView 设置为密码输入无效的问题
    一个简单的果冻弹动动画
    ios中的自动释放池
    ios 静态库联合调试
    【转】IOS制作静态库
    objective-c中为什么不能实现多重继承及如何变通实现
    回调中释放自己会不会导致崩溃?
    【转载】Objective-C runtime 消息机制
  • 原文地址:https://www.cnblogs.com/Blogggggg/p/9074908.html
Copyright © 2011-2022 走看看