zoukankan      html  css  js  c++  java
  • SDOI2010 粟粟的书架 lg2468(可持久化,前缀和)

    题面见https://www.luogu.org/problemnew/show/P2468

    然后这道题属于合二为一题,看一眼数据范围就能发现

    首先我们先考虑50分,二维前缀和维护一下(反正我不记得公式,手推了半天)

    tot[i][j][k]表示矩阵(1,1)到(i,j)中数值大等于k的总和

    num[i][j][k]表示矩阵(1,1)到(i,j)中数值大等于k的个数

    那么做法也就显而易见了,二分k的值进行check

    最后注意一个小问题,就是有可能一个k值有多个点,而我不需要全选就能满足条件,这个可以自行理解一下

    后百分之五十,一开始口胡了一个一维前缀和的做法,貌似是两个log,然而我在学可持久化数据结构,不能偷懒

    思考了一下,开一棵权值线段树,把它变成主席树,根x代表插入了第x个数后的情况

    然后建树,更新都是裸的操作

    关于查询我的想法我写在了代码里,想不通的可以看一下

    // luogu-judger-enable-o2
    #include<bits/stdc++.h>
    using namespace std;
    inline int read(){
        int w=0,f=1;
        char ch=getchar();
        while(ch<'0'||ch>'9'){
            if(ch=='-') f=-1;
            ch=getchar();
        }
        while(ch>='0'&&ch<='9'){
            w=(w<<3)+(w<<1)+ch-48;
            ch=getchar();
        }
        return w*f;
    }
    int n,m,q,a[210][210],tot[210][210][1010],num[210][210][1010],ans,cnt,root[5000010],b[5000010];
    int get_sum(int x1,int y1,int x2,int y2,int k,int f)
    {
        if(f==1)return tot[x2][y2][k]-tot[x2][y1-1][k]-tot[x1-1][y2][k]+tot[x1-1][y1-1][k];
        else return num[x2][y2][k]-num[x2][y1-1][k]-num[x1-1][y2][k]+num[x1-1][y1-1][k];
    }
    inline void work2(){//二维前缀和大力维护,口胡了一下写在上面了,就不多解释了,着重看主席树 
        int i,j,k,maxx=0;
        for(i=1;i<=n;i++){
            for(j=1;j<=m;j++){
                a[i][j]=read();maxx=max(maxx,a[i][j]);
            }
        }
        for(k=0;k<=maxx;k++){
            for(i=1;i<=n;i++){
                for(j=1;j<=m;j++){
                    tot[i][j][k]=tot[i-1][j][k]+tot[i][j-1][k]-tot[i-1][j-1][k]+(a[i][j]>=k)*a[i][j];
                    num[i][j][k]=num[i-1][j][k]+num[i][j-1][k]-num[i-1][j-1][k]+(a[i][j]>=k);
                }
            }
        }
        while(q--){
            int x1,y1,x2,y2,h;
            x1=read();y1=read();x2=read();y2=read();h=read();
            if(get_sum(x1,y1,x2,y2,0,1)<h) puts("Poor QLW");
            else{
                int l=0,r=maxx+1;ans=-1;
                while(l<=r){
                    int mid=(l+r)>>1;
                    if(get_sum(x1,y1,x2,y2,mid,1)>=h){
                        ans=mid;l=mid+1;
                    }
                    else r=mid-1;
                }
                printf("%d
    ",get_sum(x1,y1,x2,y2,ans,2)-(get_sum(x1,y1,x2,y2,ans,1)-h)/ans);
            }
        }
    }
    struct Node{
        int ls,rs,sum,size;
    }st[50000010];
    inline int build(int l,int r){
        int pos=cnt++;
        if(l==r) return pos;
        int mid=(l+r)>>1;
        st[pos].ls=build(l,mid);
        st[pos].rs=build(mid+1,r);
        return pos;
    }//常规建树 
    inline int update(int tim,int l,int r,int x){//tim表示历史版本,l,r为范围,x为我当前插入的数 
        int pos=cnt++;
        st[pos]=st[tim];st[pos].size++;st[pos].sum+=x;//这个节点的size+1,sum+=x 
        if(l==r) return pos;//到叶子了,大力返回就好 
        int mid=(l+r)>>1;
        if(x<=mid) st[pos].ls=update(st[tim].ls,l,mid,x);
        else st[pos].rs=update(st[tim].rs,mid+1,r,x);
        return pos;
    }
    inline int query(int l,int r,int fir,int sec,int w){//具体解释见下方 
        if(l==r) return (w-1)/l+1;//可能不会整除,就这么处理一下就好了 
        int mid=(l+r)>>1;int x=st[st[sec].rs].sum-st[st[fir].rs].sum;
        if(w<=x) return query(mid+1,r,st[fir].rs,st[sec].rs,w);
        else return st[st[sec].rs].size-st[st[fir].rs].size+query(l,mid,st[fir].ls,st[sec].ls,w-x);
    }
    /*
    这棵主席树是基于权值线段树的,权值的范围只有1k 
    主席树维护了历史版本的权值线段树上的size和sum 
    然后关于建树和更新都没什么新意
    查询这个我一开始不能很好的理解,那么我现在稍微解释一下我的思路
    首先l,r,fir,sec,w分别表示区间,版本号,还需要多少值
    然后大多数题查询的时候都是向左子树查一下,比一下大小
    这里查右子树是因为这是一棵权值线段树,我们希望尽量少地选点,也就意味着选的数要尽可能大
    那么能选右子树(也就是值更大的点),当然选大的啊
    如果右子树总和够,就往右子树走,不够的话,算上右子树,往左子树走 
    */
    inline void work1()
    {
        int maxw=-1e9-7;
        for(int i=1;i<=m;i++) b[i]=read(),maxw=max(b[i],maxw);
        root[0]=build(1,maxw+10);
        for(int i=1;i<=m;i++) root[i]=update(root[i-1],1,maxw,b[i]);
        for(int i=1;i<=q;i++)
        {
            int y1,y2,h;y1=read(),y1=root[read()-1],y2=read(),y2=root[read()],h=read();
            if(st[y2].sum-st[y1].sum<h){puts("Poor QLW");continue;}
            printf("%d
    ",query(1,maxw,y1,y2,h));
        }
    }
    int main(){
        n=read();m=read();q=read();
        if(n!=1){//合二为一辣鸡题 
            work2();
        }
        else work1();
        return 0;
    }
  • 相关阅读:
    Redis网络连接库剖析
    如何下载和安装pywin32
    Python游戏开发入门:pygame事件处理机制
    python常见错误
    波特率与比特率
    __gcd最大公约数
    动态规划算法之矩阵连乘问题
    二分插入排序+二分搜索
    office 总结
    javaWeb总结
  • 原文地址:https://www.cnblogs.com/wenci/p/10158661.html
Copyright © 2011-2022 走看看