SDOI2010粟粟的书架

题目传送:https://www.luogu.org/problemnew/show/P2468

这是一个二合一的题目,前50% \(n!=1\)的分数中,我们考虑用动态规划来做。
\(sum[i][j][k]\)表示大于等于k的\([1,1]\)\([i,j]\)范围内的数的总和,然后用\(num[i][j][k]\)表示数的个数。
之后就可以用二维前缀和来维护了,具体见代码subtask1

后50%的题目因为数据范围很大,所以我们只能考虑使用log复杂度的数据结构,因为还涉及到区间排名的问题,所以自然查询的时候要换成主席树。
不需要离散化。在查询的时候因为是查找大于等于当前查找值的数的个数,所以我们要从右子树开始查找(显然是要从大到小),之后如果右子树的总和大于等于当前查找值,那么就进入右子树,如果不是,那么加上右子树的数的个数然后查找左子树。之后要注意的是如果到了叶子节点,我们要求的个数等于还需要的总和除以该节点值的向上取整。
具体见代码subtask2

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define MAXN 1000*10+500000*15+10
int n,m,kkk;
namespace subtask1
{
    int maxx=0;
    int sum[210][210][1010],num[210][210][1010],a[1010][1010];
    inline int max(int x,int y){return x>y?x:y;}
    inline int getsum(int x1,int x2,int y1,int y2,int k){return sum[x2][y2][k]-sum[x1-1][y2][k]-sum[x2][y1-1][k]+sum[x1-1][y1-1][k];}
    inline int getnum(int x1,int x2,int y1,int y2,int k){return num[x2][y2][k]-num[x1-1][y2][k]-num[x2][y1-1][k]+num[x1-1][y1-1][k];}
    inline void init()
    {
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
                scanf("%d",&a[i][j]),maxx=max(maxx,a[i][j]);
        for(int k=0;k<=maxx;k++)
            for(int i=1;i<=n;i++)
                for(int j=1;j<=m;j++)
                {
                    sum[i][j][k]=sum[i][j-1][k]+sum[i-1][j][k]-sum[i-1][j-1][k]+(a[i][j]>=k?a[i][j]:0);
                    num[i][j][k]=num[i][j-1][k]+num[i-1][j][k]-num[i-1][j-1][k]+(a[i][j]>=k?1:0);
                }
    }
    inline void work(int x1,int x2,int y1,int y2,int h)
    {
        if(getsum(x1,x2,y1,y2,0)<h) 
        {
            printf("Poor QLW\n");
            return;
        }
        int l=0,r=maxx+1,ans=-0x3f;
        while(l<r)
        {
            int mid=(l+r)>>1;
            if(getsum(x1,x2,y1,y2,mid)>=h) ans=mid,l=mid+1;
            else r=mid;
        }
        if(ans==-0x3f) 
        {
            printf("Poor QLW\n");
            return;
        }
        printf("%d\n",getnum(x1,x2,y1,y2,ans)-(getsum(x1,x2,y1,y2,ans)-h)/ans); 
    }    
}
namespace subtask2
{
    int cnt;
    int sum[MAXN],siz[MAXN],ls[MAXN],rs[MAXN],rt[MAXN],a[500010];
    inline int max(int x,int y){return x>y?x:y;}
    inline void build(int &x,int l,int r)
    {
        x=++cnt;
        if(l==r) return;
        int mid=(l+r)>>1;
        build(ls[x],l,mid);
        build(rs[x],mid+1,r);
    }
    inline int modify(int x,int l,int r,int p)
    {
        int to=++cnt;
        ls[to]=ls[x],rs[to]=rs[x],siz[to]=siz[x]+1,sum[to]=sum[x]+p;
        if(l==r) return to;
        int mid=(l+r)>>1;
        if(mid<p) rs[to]=modify(rs[to],mid+1,r,p);
        else ls[to]=modify(ls[to],l,mid,p);
        return to;
    }
    inline int query(int u,int v,int l,int r,int k)
    {
        if(l==r) return (k+l-1)/l;
        int mid=(l+r)>>1;
        int x=sum[rs[v]]-sum[rs[u]];
        if(k<=x) return query(rs[u],rs[v],mid+1,r,k);
        else return siz[rs[v]]-siz[rs[u]]+query(ls[u],ls[v],l,mid,k-x);
    }
    inline void work()
    {
        int maxx=0;
        for(int i=1;i<=m;i++) scanf("%d",&a[i]),maxx=max(maxx,a[i]);
        build(rt[0],1,m);
        for(int i=1;i<=m;i++) rt[i]=modify(rt[i-1],1,maxx,a[i]);
        for(int i=1;i<=kkk;i++)
        {
            int cur1,cur2,u,v,h;
            scanf("%d",&cur1),scanf("%d",&u);
            scanf("%d",&cur2),scanf("%d",&v);
            scanf("%d",&h);
            if(sum[rt[v]]-sum[rt[u-1]]<h)
            {
                printf("Poor QLW\n");
                continue;
            }
            printf("%d\n",query(rt[u-1],rt[v],1,maxx,h));
        }
    }
}
using namespace std;
using namespace subtask1;
using namespace subtask2;
int main()
{
    scanf("%d%d%d",&n,&m,&kkk);
    if(n!=1)
    {
        subtask1::init();
        for(int i=1;i<=kkk;i++)
        {
            int xx1,xx2,yy1,yy2;
            int val;
            scanf("%d%d%d%d%d",&xx1,&yy1,&xx2,&yy2,&val);
            subtask1::work(xx1,xx2,yy1,yy2,val);
        }
    }
    else
        subtask2::work();
    return 0;
}
posted @ 2018-12-06 21:39  风浔凌  阅读(132)  评论(0编辑  收藏  举报