bzoj4408: [Fjoi 2016]神秘数

Posted Soda

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bzoj4408: [Fjoi 2016]神秘数相关的知识,希望对你有一定的参考价值。

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]所构成的可重复数字集合的神秘数。

Input

第一行一个整数n,表示数字个数。
第二行n个整数,从1编号。
第三行一个整数m,表示询问个数。
以下m行,每行一对整数l,r,表示一个询问。

Output

对于每个询问,输出一行对应的答案。

Sample Input

5
1 2 4 9 10
5
1 1
1 2
1 3
1 4
1 5

Sample Output

2
4
8
8
8

HINT

 

对于100%的数据点,n,m <= 100000,∑a[i] <= 10^9

#include<iostream>
#include<cstdio>
#define maxn 100010
using namespace std;
int rc[maxn*40],lc[maxn*40],sum[maxn*40],a[maxn],root[maxn];
int n,m,mx,tot;
void build(int x,int &y,int l,int r,int v){
    if(!y)y=++tot;
    sum[y]=sum[x]+v;
    if(l==r)return;
    int mid=(l+r)>>1;
    if(v<=mid){
        rc[y]=rc[x];
        build(lc[x],lc[y],l,mid,v);
    }
    else{
        lc[y]=lc[x];
        build(rc[x],rc[y],mid+1,r,v);
    }
}
int query(int x,int y,int l,int r,int v){
    if(v>=r)return sum[y]-sum[x];
    int mid=(l+r)>>1;
    if(v>mid)return sum[lc[y]]-sum[lc[x]]+query(rc[x],rc[y],mid+1,r,v);
    else return query(lc[x],lc[y],l,mid,v);
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]),mx=max(mx,a[i]);
    for(int i=1;i<=n;i++)
        build(root[i-1],root[i],1,mx,a[i]);
    scanf("%d",&m);
    for(int i=1;i<=m;i++){
        int l,r,ans=1;
        scanf("%d%d",&l,&r);
        while(1){
            int sum=query(root[l-1],root[r],1,mx,ans);
            if(sum<ans)break;
            ans=sum+1;
        }
        printf("%d\n",ans);
    }
    return 0;
}

 

以上是关于bzoj4408: [Fjoi 2016]神秘数的主要内容,如果未能解决你的问题,请参考以下文章

BZOJ4408[Fjoi 2016]神秘数 主席树神题

●BZOJ BZOJ 4408 [Fjoi 2016]神秘数

Bzoj 4408: [Fjoi 2016]神秘数 可持久化线段树,神题

bzoj4408: [Fjoi 2016]神秘数

bzoj4408[Fjoi 2016]神秘数 主席树

bzoj 4408: [Fjoi 2016]神秘数