BZOJ1926 [SDOI2010]粟粟的书架

题面

题意

1.给出一个矩形(200*200),每次询问给出一个子矩形和一个数,问在这个子矩形中至少选几个数,使它们的和大于等于给出的数。
2.给出一列数(500000),每次询问给出一个区间和一个数,问在这个区间中至少选几个数,使它们的和大于等于给出的数。

做法

考虑二分选出数的最小值,这样问题就转化为了:每次判断某个范围内大于等于某数的和是否大于等于给出数。
1.只要用二维前缀和维护即可:
sum[i][j][k]表示(1,1)与(i,j)构成的矩形中大于等于k的数的和
cnt[i][j][k]表示(1,1)与(i,j)构成的矩形中大于等于k的数的个数
2.用主席树维护每一个最小值下的线段树,每次查询区间和即可

代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#define M 1010
#define MN 1000
using namespace std;

int n,m,Q;

#define N 210
namespace solve1
{
    int num[N][N],sum[N][N][M],cnt[N][N][M],a,b,c,d,e,ans;
    inline int judge(int u)
    {
        return sum[c][d][u]+sum[a-1][b-1][u]-sum[a-1][d][u]-sum[c][b-1][u]-e;
    }
    inline int ask(int u)
    {
        return cnt[c][d][u]+cnt[a-1][b-1][u]-cnt[a-1][d][u]-cnt[c][b-1][u];
    }
    void work()
    {
        int i,j,k,l,r,mid,tmp,t;
        for(i=1; i<=m; i++)
        {
            for(j=1; j<=n; j++)
            {
                scanf("%d",&num[i][j]);
            }
        }
        for(k=1; k<=MN; k++)
        {
            for(i=1; i<=m; i++)
            {
                for(j=1; j<=n; j++)
                {
                    if(num[i][j]>=k) sum[i][j][k]=num[i][j],cnt[i][j][k]=1;
                    sum[i][j][k]+=sum[i-1][j][k]+sum[i][j-1][k]-sum[i-1][j-1][k];
                    cnt[i][j][k]+=cnt[i-1][j][k]+cnt[i][j-1][k]-cnt[i-1][j-1][k];
                }
            }
        }
        for(i=1; i<=Q; i++)
        {
            scanf("%d%d%d%d%d",&a,&b,&c,&d,&e);
            for(l=1,r=MN+1; l<r;)
            {
                mid=((l+r)>>1);
                judge(mid)>=0?l=mid+1:r=mid;
            }
            l--;
            if(!l)
            {
                puts("Poor QLW");
                continue;
            }
            ans=ask(l);
            t=ans-ask(l+1);
            tmp=judge(l);
            for(; t&&tmp-l>=0; t--,ans--,tmp-=l);
            printf("%d\n",ans);
        }
    }
}
#undef N

#define N 500100
namespace solve2
{
    int num[N],pos[N],tt,lt,rt[M],a,b,e,ans;
    struct Node
    {
        int ls,rs,sum,cnt;
    } node[N*20];

    inline bool cmp(int u,int v)
    {
        return num[u]>num[v];
    }

    inline void up(int u)
    {
        int L=node[u].ls,R=node[u].rs;
        node[u].sum=node[L].sum+node[R].sum;
        node[u].cnt=node[L].cnt+node[R].cnt;
    }

    void build(int now,int l,int r)
    {
        if(l==r) return;
        int mid=((l+r)>>1);
        node[now].ls=++tt;
        build(tt,l,mid);
        node[now].rs=++tt;
        build(tt,mid+1,r);
    }

    void add(int now,int l,int r,int u)
    {
        if(l==r)
        {
            node[now].cnt=1;
            node[now].sum=num[u];
            return;
        }
        int mid=((l+r)>>1);
        if(u<=mid)
        {
            if(node[now].ls<=lt)
            {
                node[++tt]=node[node[now].ls];
                node[now].ls=tt;
            }
            add(node[now].ls,l,mid,u);
        }
        else
        {
            if(node[now].rs<=lt)
            {
                node[++tt]=node[node[now].rs];
                node[now].rs=tt;
            }
            add(node[now].rs,mid+1,r,u);
        }
        up(now);
    }

    int asks(int now,int l,int r,int u,int v)
    {
        if(l==u&&v==r) return node[now].sum;
        int mid=((l+r)>>1),res=0;
        if(u<=mid) res+=asks(node[now].ls,l,mid,u,min(mid,v));
        if(mid<v) res+=asks(node[now].rs,mid+1,r,max(u,mid+1),v);
        return res;
    }

    int askc(int now,int l,int r,int u,int v)
    {
        if(l==u&&v==r) return node[now].cnt;
        int mid=((l+r)>>1),res=0;
        if(u<=mid) res+=askc(node[now].ls,l,mid,u,min(mid,v));
        if(mid<v) res+=askc(node[now].rs,mid+1,r,max(u,mid+1),v);
        return res;
    }

    void work()
    {
        int i,j,l,r,mid,t,tmp;
        for(i=1; i<=n; i++) scanf("%d",&num[i]),pos[i]=i;
        sort(pos+1,pos+n+1,cmp);
        rt[MN+1]=1;
        build(++tt,1,n);
        for(i=MN,j=1; i>=1;i--)
        {
            lt=tt;
            rt[i]=++tt;
            node[tt]=node[rt[i+1]];
            for(; num[pos[j]]==i;j++)
            {
                add(rt[i],1,n,pos[j]);
            }
        }
        for(i=1;i<=Q;i++)
        {
            scanf("%*d%d%*d%d%d",&a,&b,&e);
            for(l=1,r=MN+1;l<r;)
            {
                mid=((l+r)>>1);
                asks(rt[mid],1,n,a,b)>=e?l=mid+1:r=mid;
            }
            l--;
            if(!l)
            {
                puts("Poor QLW");
                continue;
            }
            ans=askc(rt[l],1,n,a,b);
            t=ans-askc(rt[l+1],1,n,a,b);
            tmp=asks(rt[l],1,n,a,b)-e;
            for(; t&&tmp-l>=0; t--,ans--,tmp-=l);
            printf("%d\n",ans);
        }
    }
}

int main()
{
    int i,j;
    cin>>m>>n>>Q;
    if(m>1) solve1::work();
    else solve2::work();
}
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页