[BZOJ 4408][Fjoi 2016]神秘数(主席树+思路)

Description

一个可重复数字集合S的神秘数定义为最小的不能被S的子集的和表示的正整数。例如S={1,1,1,4,13},

1 = 1

2 = 1+1

3 = 1+1+1

4 = 4

5 = 4+1

6 = 4+1+1

7 = 4+1+1+1

8无法表示为集合S的子集的和,故集合S的神秘数为8。

现给定n个正整数a[1]..a[n],m个询问,每次询问给定一个区间[l,r](l<=r),求由a[l],a[l+1],…,a[r]所构成的可重复数字集合的神秘数。

Solution

思路蛮神的QAQ

如果有一个集合的神秘数为ans,当我们想要新加入一个数a时可以发现[a,a+ans-1]可以被表示

如果ans<a则[1,ans-1]与[a,a+ans-1]不连续,答案仍是ans

如果ans>=a则神秘数被更新为a+ans

于是可以用这样的方法求神秘数,当∑ai(ai<=ans)>=ans时(也就是说ans不大于排序后的前缀和,说明还有小于等于ans的数可以加入),∑ai(ai<=ans)+1被作为神秘数并且迭代下去,否则答案就是ans

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define MAXN 100005
using namespace std;
int n,m,a[MAXN],b[MAXN],rt[MAXN];
int tot=0,ls[MAXN*20],rs[MAXN*20],v[MAXN*20];
int read()
{
    int x=0,f=1;char c=getchar();
    while(c<'0'||c>'9'){
        if(c=='-')f=-1;c=getchar();
    }
    while(c>='0'&&c<='9'){
        x=x*10+c-'0';c=getchar();
    }
    return x*f;
}
void build(int &idx,int l,int r)
{
    tot++,idx=tot;v[idx]=0;
    if(l==r)return;
    int mid=(l+r)>>1;
    build(ls[idx],l,mid);
    build(rs[idx],mid+1,r);
}
void insert(int &idx,int last,int l,int r,int pos,int val)
{
    tot++,idx=tot;
    v[idx]=v[last]+val;
    ls[idx]=ls[last],rs[idx]=rs[last];
    if(l==r)return;
    int mid=(l+r)>>1;
    if(pos<=mid)insert(ls[idx],ls[last],l,mid,pos,val);
    else insert(rs[idx],rs[last],mid+1,r,pos,val);
}
int query(int a,int b,int l,int r,int L,int R)
{
    if(L<=l&&R>=r)return v[a]-v[b];
    int mid=(l+r)>>1;
    if(R<=mid)return query(ls[a],ls[b],l,mid,L,R);
    else if(L>mid)return query(rs[a],rs[b],mid+1,r,L,R);
    else return query(ls[a],ls[b],l,mid,L,R)+query(rs[a],rs[b],mid+1,r,L,R);
}
int main()
{
    n=read();
    for(int i=1;i<=n;i++)b[i]=a[i]=read();
    sort(b+1,b+1+n);
    int sz=unique(b+1,b+1+n)-b-1;
    build(rt[0],1,sz);
    for(int i=1;i<=n;i++)
    {
        int pos=lower_bound(b+1,b+1+sz,a[i])-b;
        insert(rt[i],rt[i-1],1,sz,pos,a[i]);
    }
    m=read();
    for(int i=1;i<=m;i++)
    {
        int l=read(),r=read();
        int k=1,pos=upper_bound(b+1,b+1+sz,k)-b-1;
        int get=query(rt[r],rt[l-1],1,sz,1,pos);
        while(get>=k)
        {
            k=get+1;
            pos=upper_bound(b+1,b+1+sz,k)-b-1;
            get=query(rt[r],rt[l-1],1,sz,1,pos);
        }
        printf("%d\n",k);
    }
    return 0;
}

 

转载于:https://www.cnblogs.com/Zars19/p/6909869.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值