bzoj 4408

嗯..

首先考虑如果只有一次询问我们怎么做

设我们当前有个数集{$S$},进行这一询问,我们怎么处理?

首先不妨假设{$S$}单调不降(如果不是这样的话显然排序并不会影响答案)

那么假设前$i$个数都合法,其能组合出的最大的值为$lim$,那么我们只需比较$S_{i+1}$与$lim+1$的大小就可以确定答案是否增大

为什么?

前$i$个数能够表示出$lim$,而如果$S_{i+1}<=lim$,那么一定能表示出$[lim+1,lim+S_{i+1}]$!

为什么?

我们钦定$S_{i+1}$必选,那么我们为了表示出$[lim+1,lim+S_{i+1}]$这些数,我们只需用$1~i$的数表示出$[lim+1-S_{i+1},lim]$即可

考虑到要求$1~i$一定能表示出$[0,lim]$,因此只需要求$lim+1-S_{i+1}\geq 0$即可

也即$S_{i+1}\leq lim+1$

因此如果只有一次询问,我们只需从前向后扫即可

但是现在是多次询问啊!

没有关系,我们考虑上面操作的等价条件:

我们发现,上面操作算出的答案一定满足:将所有小于等于答案的原集合中的数求和等于答案-1!

因此我们只需利用这一性质即可

从1开始枚举一个答案,然后检验区间中小于等于这个答案的数求和是否大于这个答案即可

为了求出区间小于等于某个答案的数的和,需要用可持久化的权值线段树实现

代码很好写

#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <queue>
#include <stack>
#define ll long long
#define ls tree[rt].lson
#define rs tree[rt].rson
using namespace std;
const int lim=1000000000;
struct Pre_Seg_Tree
{
    int lson,rson;
    ll sum;
}tree[8000005];
ll v[100005];
int rot[100005];
int n,m,tot;
void update(int rt)
{
    tree[rt].sum=tree[ls].sum+tree[rs].sum;
}
void ins(int &rt,int lrt,int l,int r,ll pos)
{
    rt=++tot;
    if(l==r){tree[rt].sum=tree[lrt].sum+1ll*pos;return;}
    int mid=(l+r)>>1;
    if(pos<=mid)rs=tree[lrt].rson,ins(ls,tree[lrt].lson,l,mid,pos);
    else ls=tree[lrt].lson,ins(rs,tree[lrt].rson,mid+1,r,pos);
    update(rt);
}
ll query(int rt1,int rt2,int l,int r,int lq,int rq)
{
    if(l>=lq&&r<=rq)return tree[rt2].sum-tree[rt1].sum;
    int mid=(l+r)>>1;
    ll s=0;
    if(lq<=mid)s+=query(tree[rt1].lson,tree[rt2].lson,l,mid,lq,rq);
    if(rq>mid)s+=query(tree[rt1].rson,tree[rt2].rson,mid+1,r,lq,rq);
    return s;
}
template <typename T> inline void read(T &x)
{
    T f=1,c=0;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){c=c*10+ch-'0';ch=getchar();}
    x=c*f;
}
int main()
{
    read(n);
    for(int i=1;i<=n;i++)read(v[i]),ins(rot[i],rot[i-1],1,lim,v[i]);
    read(m);
    while(m--)
    {
        int l,r;
        read(l),read(r);
        ll ans=1;
        while(1)
        {
            ll sum=query(rot[l-1],rot[r],1,lim,1,ans);
            if(sum<ans)break;
            ans=sum+1;
        }
        printf("%lld\n",ans);
    }
    return 0;
}

 

posted @ 2019-06-26 11:14  lleozhang  Views(155)  Comments(0Edit  收藏  举报
levels of contents