【Luogu】P2468粟粟的书架(主席树+前缀和)

  题目链接

  我仿佛中了个爆零debuff

  本题分成两部分,五十分用前缀和,f[i][j][k]表示(1,1)到(i,j)的矩形大于等于k的有多少个数(再记录页数和),查询时二分,另外的用主席树,类似方法二分求解,

  细节很多需要注意。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cctype>
#include<cstdlib>
#define maxr 220
#define maxc 550000
#define maxv 1000
using namespace std;
inline long long read(){
    long long num=0,f=1;
    char ch=getchar();
    while(!isdigit(ch)){
        if(ch=='-')    f=-1;
        ch=getchar();
    }
    while(isdigit(ch)){
        num=num*10+ch-'0';
        ch=getchar();
    }
    return num*f;
}

int d[maxr][maxr][maxv+10],s[maxr][maxr][maxv+10];
int tree[maxc*10],root[maxc],ls[maxc*10],rs[maxc*10],tot,size[maxc*10];
int q[maxr][maxr];

int r,c,m;

int asks(int xa,int ya,int xb,int yb,int x){    return d[xb][yb][x]-d[xa-1][yb][x]-d[xb][ya-1][x]+d[xa-1][ya-1][x];    }
int askn(int xa,int ya,int xb,int yb,int x){    return s[xb][yb][x]-s[xa-1][yb][x]-s[xb][ya-1][x]+s[xa-1][ya-1][x];    }

void solve1(){
    for(int i=1;i<=r;++i)
        for(int j=1;j<=c;++j)    q[i][j]=read();
    for(int k=0;k<=maxv;++k)
        for(int i=1;i<=r;++i)
            for(int j=1;j<=c;++j){
                bool flag=0;
                if(q[i][j]<k)    flag=1;;
                d[i][j][k]=d[i-1][j][k]+d[i][j-1][k]-d[i-1][j-1][k]+(flag==0?q[i][j]:0);
                s[i][j][k]=s[i-1][j][k]+s[i][j-1][k]-s[i-1][j-1][k]+(flag==0?1:0);
            }
    while(m--){
        int xa=read(),ya=read(),xb=read(),yb=read(),x=read();
        if(asks(xa,ya,xb,yb,0)<x){
            printf("Poor QLW\n");
            continue;
        }
        int lef=0,rig=maxv,ans=-1;
        while(lef<=rig){
            int mid=(lef+rig)>>1;
            //printf("%d>>>%d\n",asks(xa,ya,xb,yb,mid),mid);
            if(asks(xa,ya,xb,yb,mid)>=x){
                ans=mid;
                lef=mid+1;
            }
            else    rig=mid-1;
        }
        if(ans==-1)    printf("Poor QLW\n");
        else        printf("%d\n",askn(xa,ya,xb,yb,ans)-(asks(xa,ya,xb,yb,ans)-x)/ans);
    }
}

inline void update(int &o,int last,int lef,int rig,int p){
    o=++tot;    ls[o]=ls[last];    rs[o]=rs[last];    tree[o]=tree[last]+p;    size[o]=size[last]+1;
    if(lef==rig)    return;
    int mid=(lef+rig)>>1;
    if(p<=mid)    update(ls[o],ls[last],lef,mid,p);
    else        update(rs[o],rs[last],mid+1,rig,p);
}

void solve2(){
    for(int i=1;i<=c;++i){
        int x=read();
        update(root[i],root[i-1],1,maxv,x);
    }
    while(m--){
        int xa=read(),ya=read(),xb=read(),yb=read(),x=read();
        if(tree[root[yb]]-tree[root[ya-1]]<x){
            printf("Poor QLW\n");
            continue;
        }
        register int lef=1,rig=maxv,a=root[ya-1],b=root[yb],ans=0;
        while(lef<rig){
            register int mid=(lef+rig)>>1;
            if(tree[rs[b]]-tree[rs[a]]>=x){
                lef=mid+1;
                a=rs[a];    b=rs[b];
            }
            else{
                x-=tree[rs[b]]-tree[rs[a]];
                ans+=size[rs[b]]-size[rs[a]];
                rig=mid;
                a=ls[a];    b=ls[b];
            }
        }
        ans+=(x+lef-1)/lef;
        printf("%d\n",ans);
    }
    return;
}
            

int main(){
    r=read(),c=read(),m=read();
    if(r!=1)    solve1();
    else        solve2();
    return 0;
}    

猜你喜欢

转载自www.cnblogs.com/cellular-automaton/p/8916344.html