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,求由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
题解
对于可以取到的区间[1,r],我们新增加一个数x,如果x∈[1,ans]则可取区间变为[1,r+x]
对于一个区间,我们假设神秘数为ans(初始ans=1)
每次把区间内小于等于ans的数累加=sum 如果sum> ans 则说明[1,sum]是可取到达,ans变为sum+1
否则说明ans已经是最小的不能取到的神秘数了。
代码
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<set>
#include<complex>
#define ll long long
#define mod 10000007
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int n,tot,a[100005],t[10000005],rt[100005],ls[10000005],rs[10000005],sz;
void insert(int &k,int p,int l,int r,int x)
{
t[k=++sz]=t[p]+x;
ls[k]=ls[p];rs[k]=rs[p];
if (l==r) return;
int mid=(l+r)>>1;
if (x<=mid) insert(ls[k],ls[p],l,mid,x);
else insert(rs[k],rs[p],mid+1,r,x);
}
int query(int k1,int k2,int l,int r,int x)
{
if (r<=x) return t[k1]-t[k2];
int mid=(l+r)>>1;
if (x<=mid) return query(ls[k1],ls[k2],l,mid,x);
else return t[ls[k1]]-t[ls[k2]]+query(rs[k1],rs[k2],mid+1,r,x);
}
int main()
{
n=read();
for (int i=1;i<=n;i++) a[i]=read(),tot+=a[i];
for (int i=1;i<=n;i++) insert(rt[i],rt[i-1],1,tot,a[i]);
int Case=read();
while (Case--)
{
int l=read(),r=read(),ans=1;
while (1)
{
int sum=query(rt[r],rt[l-1],1,tot,ans);
if (sum<ans) break;
ans=sum+1;
}
printf("%d\n",ans);
}
return 0;
}