BZOJ 1926: [Sdoi2010]粟粟的书架【二分+主席树】

1926: [Sdoi2010]粟粟的书架

【题目描述】
传送门

【题解】

这题肯定是让我们分类讨论的。
R,C≤200时,荣斥+二分查找:
Val[i][j][k]表示从(1,1)到(i,j)大于k的和,Num[i][j][k]表示从(1,1)到(i,j)大于k的个数,那么我们二分这个k,荣斥套一下就可以了。
不过要注意,当k有多个时,不一定要全部用掉,所以还要减去没用的。
R=1,C≤500000时,主席树套一下就可以了。

代码如下

#include<cstdio>
#include<cctype>
#define MAXN 500005
using namespace std;
int n,m,q,cnt,Ans1,Ans2,Ans3,mp[205][205],Val[205][205][1005],Num[205][205][1005];
int x1,y1,x2,y2,K,T[MAXN],Tre[MAXN*20],Sum[MAXN*20],L[MAXN*20],R[MAXN*20];
bool check(int x){
    if(Val[x2][y2][x]-Val[x2][y1-1][x]-Val[x1-1][y2][x]+Val[x1-1][y1-1][x]>=K){
        Ans1=Val[x2][y2][x]-Val[x2][y1-1][x]-Val[x1-1][y2][x]+Val[x1-1][y1-1][x];
        Ans2=Num[x2][y2][x]-Num[x2][y1-1][x]-Num[x1-1][y2][x]+Num[x1-1][y1-1][x];
        return 1;
    }else return 0;
}
int Updata(int lst,int l,int r,int x){
    int rt=++cnt;
    L[rt]=L[lst];R[rt]=R[lst];Tre[rt]=Tre[lst]+1;Sum[rt]=Sum[lst]+x;
    if(l<r){
        int mid=(r+l)>>1;
        if(x<=mid) L[rt]=Updata(L[lst],l,mid,x);
        else R[rt]=Updata(R[lst],mid+1,r,x);
    }
    return rt;
}
int Query(int u,int v,int l,int r,int p){
    int Ans=0;
    while(l<r){
        int Now=Sum[R[v]]-Sum[R[u]],mid=(r+l)>>1;
        if(p<=Now) u=R[u],v=R[v],l=mid+1;
        else Ans+=Tre[R[v]]-Tre[R[u]],p-=Now,r=mid,u=L[u],v=L[v];
    }
    Ans+=(p+l-1)/l;
    return Ans;
}
int read(){
    int ret=0;char ch=getchar();bool f=1;
    for(;!isdigit(ch);ch=getchar()) f^=!(ch^'-');
    for(; isdigit(ch);ch=getchar()) ret=(ret<<3)+(ret<<1)+ch-48;
    return f?ret:-ret;
}
int main(){
    #ifndef ONLINE_JUDGE
    freopen("1926.in","r",stdin);
    freopen("1926.out","w",stdout);
    #endif
    n=read(),m=read(),q=read();
    if(n>1){
        for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++) mp[i][j]=read();
        for(int k=1000;k;k--)
        for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
        Val[i][j][k]=Val[i-1][j][k]+Val[i][j-1][k]-Val[i-1][j-1][k]+(mp[i][j]>=k?mp[i][j]:0),
        Num[i][j][k]=Num[i-1][j][k]+Num[i][j-1][k]-Num[i-1][j-1][k]+(mp[i][j]>=k);
        for(int i=1;i<=q;i++){
            Ans1=-1;
            x1=read(),y1=read(),x2=read(),y2=read(),K=read();
            if(!check(1)){printf("Poor QLW\n");continue;}
            for(int L=1,R=1000,mid=(R+L)>>1;L<=R;mid=(R+L)>>1)
            if(check(mid)) Ans3=mid,L=mid+1;else R=mid-1;
            printf("%d\n",Ans2-(Ans1-K)/Ans3);
        }
    }else{
        for(int i=1;i<=m;i++){
            int x=read();
            T[i]=Updata(T[i-1],1,1000,x);
        }
        for(int i=1;i<=q;i++){
            int l,r,p;read();l=read();read();r=read();p=read();
            if(Sum[T[r]]-Sum[T[l-1]]>=p) printf("%d\n",Query(T[l-1],T[r],1,1000,p));
            else printf("Poor QLW\n");
        }
    }
    return 0;
}
posted @ 2018-07-01 17:52  XSamsara  阅读(106)  评论(0编辑  收藏  举报