线段树套treap+dp(BZOJ1926)

1926: [Sdoi2010]粟粟的书架

Time Limit: 20 Sec   Memory Limit: 552 MB
Submit: 482   Solved: 177
[ Submit][ Status]

Description

Input

第一行是三个正整数R, C, M。 接下来是一个 R行C 列的矩阵,从上到下、从左向右依次给出了每本书的页数Pi,j。 接下来 M行,第 i 行给出正整数x1i, y1i, x2i, y2i, Hi,表示第i 天的指定区域是﹙x1i, y1i﹚与﹙x2i, y2i﹚间的矩形,总页数之和要求不低于 Hi。 保证 1≤x1i≤x2i≤R,1≤y1i≤y2i≤C。

Output

有M行,第i 行回答粟粟在第 i 天时为摘到苹果至少需要拿取多少本书。如果即使取走所有书都无法摘到苹果,则在该行输出“Poor QLW”(不含引号)。

Sample Input

5 5 7
14 15 9 26 53
58 9 7 9 32
38 46 26 43 38
32 7 9 50 28
8 41 9 7 17
1 2 5 3 139
3 1 5 5 399
3 3 4 5 91
4 1 4 1 33
1 3 5 4 185
3 3 4 3 23
3 1 3 3 108

Sample Output

6
15
2
Poor QLW
9
1
3

HINT

对于 10%的数据,满足 R, C≤10;
对于 20%的数据,满足 R, C≤40;
对于 50%的数据,满足 R, C≤200,M≤200,000;
另有 50%的数据,满足 R=1,C≤500,000,M≤20,000;
对于 100%的数据,满足 1≤Pi,j≤1,000,1≤Hi≤2,000,000,000。



思路:分情况讨论,对于R!=1的来说,用dp递推一下就行了dp[i][j][k],表示坐标(0,0)到(i,j)页数为k的有多少本,查询的时候从最高页数开始暴力就行了

对于R==1的情况则要用树套树进行处理:首先二分页数,然后查询大于等于这个页数的总页数是多少

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<vector>
#include<cmath>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<algorithm>
using namespace std;
const int maxn=500010;
int a[maxn];
int R,C,M;
int tot;
int f[205][205][1005];
struct Node
{
    int ch[2];
    int r;//优先级
    int v;//值
    int s;
    int sum;
    int cnt;//自身重复次数
    void init(int val){v=val;ch[0]=ch[1]=0;s=cnt=1;r=rand();sum=0;}
    int cmp(int x)const
    {
        if(x==v)return -1;
        return x<v?0:1;
    }

}tree[maxn*20];
void maintain(int x)
{
    tree[x].s=tree[x].cnt;
    tree[x].s+=tree[tree[x].ch[0]].s+tree[tree[x].ch[1]].s;
    tree[x].sum=tree[x].cnt*tree[x].v+tree[tree[x].ch[0]].sum+tree[tree[x].ch[1]].sum;
}
void rotate(int &o,int d)
{
    int k=tree[o].ch[d^1];
    tree[o].ch[d^1]=tree[k].ch[d];
    tree[k].ch[d]=o;
    maintain(o);
    maintain(k);
    o=k;
}
void insert(int &o,int x)
{
    if(!o)
    {
        o=++tot;
        tree[o].init(x);
    }
    else
    {
        if(x==tree[o].v)tree[o].cnt++;
        else
        {
            int d=(x<tree[o].v?0:1);
            insert(tree[o].ch[d],x);
            if(tree[tree[o].ch[d]].r>tree[o].r)
                rotate(o,d^1);
        }
    }
    maintain(o);
}
void remove(int &o,int x)
{
    if(!o)return;
    int d=tree[o].cmp(x);
    if(d==-1)
    {
        int u=o;
        if(tree[o].cnt>1)tree[o].cnt--;
        else if(tree[o].ch[0]&&tree[o].ch[1])
        {
            int d2=(tree[tree[o].ch[0]].r>tree[tree[o].ch[1]].r?1:0);
            rotate(o,d2);
            remove(tree[o].ch[d2],x);
        }
        else
        {
            if(!tree[o].ch[0])o=tree[o].ch[1];
            else o=tree[o].ch[0];
        }
    }
    else remove(tree[o].ch[d],x);
    if(o)maintain(o);
}
//返回最大值
int get_max(int o)
{
    while(tree[o].ch[0])o=tree[o].ch[0];
    return tree[o].v;
}
//返回最小值
int get_min(int o)
{
    while(tree[o].ch[1])o=tree[o].ch[1];
    return tree[o].v;
}
//返回val的前驱,如果没有的话返回y
//y的初值可赋成0,表示没有前驱
int get_pred(int o,int val,int y)
{
    if(!o)return y;
    if(tree[o].v<=val)//注意大于等于号
        return get_pred(tree[o].ch[1],val,tree[o].v);
    else return get_pred(tree[o].ch[0],val,y);
}
//返回val的后继,如果没有的话返回y
//y的初值可赋成0,表示没有后继
int get_succ(int o,int val,int y)
{
    if(!o)return y;
    if(tree[o].v>=val)return get_succ(tree[o].ch[0],val,tree[o].v);
    else return get_succ(tree[o].ch[1],val,y);
}
//返回第k大的元素的值
int get_kth(int o,int k)
{
    if(!o)return 0;
    if(k<=tree[tree[o].ch[0]].s)return get_kth(tree[o].ch[0],k);
    else if(k>tree[tree[o].ch[0]].s+tree[o].cnt)
        return get_kth(tree[o].ch[1],k-tree[tree[o].ch[0]].s-tree[o].cnt);
    return tree[o].v;
}
//返回val的排名
int get_rank(int o,int val)
{
    if(!o)return 0;
    int lsize=tree[tree[o].ch[0]].s;
    if(val<tree[o].v)
        return get_rank(tree[o].ch[0],val);
    else if(val>tree[o].v)
        return get_rank(tree[o].ch[1],val)+lsize+tree[o].cnt;
    return lsize+tree[o].cnt;
}
pair<int,int> getcnt(int o,int val)
{
    if(!o)return make_pair(0,0);
    int lsize=tree[tree[o].ch[0]].s;
    if(val<tree[o].v)
    {
        pair<int,int> tmp=getcnt(tree[o].ch[0],val);
        tmp.first+=tree[o].cnt+tree[tree[o].ch[1]].s;
        tmp.second+=tree[tree[o].ch[1]].sum+tree[o].v*tree[o].cnt;
        return tmp;
    }
    else if(val>tree[o].v)
        return getcnt(tree[o].ch[1],val);
    int cnt=tree[tree[o].ch[1]].s+tree[o].cnt;
    int sum=tree[tree[o].ch[1]].sum+tree[o].v*tree[o].cnt;
    return make_pair(cnt,sum);

}
struct IntervalTree
{
    int root[maxn<<2];
    void build(int o,int l,int r)
    {
        root[o]=0;
        for(int i=l;i<=r;i++)insert(root[o],a[i]);
        if(l==r)return;
        int mid=(l+r)>>1;
        build(o<<1,l,mid);
        build(o<<1|1,mid+1,r);
    }
    pair<int,int> query(int o,int l,int r,int q1,int q2,int val)
    {
        if(q1<=l&&r<=q2)return getcnt(root[o],val);
        int mid=(l+r)>>1;
        if(q2<=mid)return query(o<<1,l,mid,q1,q2,val);
        if(q1>mid)return query(o<<1|1,mid+1,r,q1,q2,val);
        pair<int,int> p,q;
        p=query(o<<1,l,mid,q1,mid,val);
        q=query(o<<1|1,mid+1,r,mid+1,q2,val);
        p.first+=q.first;
        p.second+=q.second;
        return p;
    }
}tr;
int sum[maxn];
void solve1()
{
    for(int i=1;i<=C;i++)
    {
        scanf("%d",&a[i]);
        sum[i]=sum[i-1]+a[i];
    }
    int x,x1,y1,x2,y2;
    tot=0;
    tr.build(1,1,C);
    pair<int,int> tmp;
    while(M--)
    {
        scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&x);
        if(sum[y2]-sum[y1-1]<x)
        {
            printf("Poor QLW\n");
            continue;
        }
        int l=0,r=1001;
        while(l<r)
        {
            int mid=l+(r-l+1)/2;
            tmp=tr.query(1,1,C,y1,y2,mid);
            if(tmp.second<x)r=mid-1;
            else l=mid;
        }
        tmp=tr.query(1,1,C,y1,y2,l+1);
        printf("%d\n",(x-tmp.second-1)/l+1+tmp.first);
    }
}
int getsum(int x1,int y1,int x2,int y2,int x)
{
    return f[x2][y2][x]-f[x1-1][y2][x]-f[x2][y1-1][x]+f[x1-1][y1-1][x];
}
void solve2()
{
    int x,x1,y1,x2,y2,sum;
    for(int i=1;i<=R;i++)
    {
        for(int j=1;j<=C;j++)
        {
            scanf("%d",&x);
            for(int k=1;k<=1000;k++)
                f[i][j][k]+=f[i-1][j][k]+f[i][j-1][k]-f[i-1][j-1][k];
            f[i][j][x]++;
        }

    }
    while(M--)
    {
        scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&sum);
        int cnt=0,ans=0,flag=0;
        for(int i=1000;i>0;i--)
        {
            int tmp=getsum(x1,y1,x2,y2,i);
            if(cnt+tmp*i<sum)cnt+=tmp*i,ans+=tmp;
            else
            {
                flag=1;
                ans+=(sum-cnt-1)/i+1;
                break;
            }
        }
        if(flag)printf("%d\n",ans);
        else printf("Poor QLW\n");
    }
}
int main()
{
    scanf("%d%d%d",&R,&C,&M);
    if(R==1)solve1();
    else solve2();
    return 0;
}




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值