BZOJ 4408: [Fjoi 2016]神秘数
4408: [Fjoi 2016]神秘数
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 553 Solved: 343
[Submit][Status][Discuss]
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
1 2 4 9 10
5
1 1
1 2
1 3
1 4
1 5
Sample Output
2
4
8
8
8
4
8
8
8
HINT
对于100%的数据点,n,m <= 100000,∑a[i] <= 10^9
Source
分析:
很机智的想法...貌似之前见过类似的想法...然而并没有想到..QAQ~~~
考虑当前我们有一个数字集合可以表示区间$[1,x]$内的数字,考虑新加入一个数字$y$,分以下两种情况:
$1.$ $y<=x+1$,那么我们发现新的数字集合可以表示区间$[1,x+y]$内的所有数字
$2.$ $y>x+1$,那么$x+1$这个数字不能被表示出来,所以$x+1$就是答案...
所以对于每个区间,我们先选定$ans=1$作为空集的神秘数,也就是说当前的集合可以表示区间$[0,ans-1]$,然后把区间内所有小于等于$ans$的数字全部加起来得到$sum$,如果$sum<ans$,那么就代表不存在新的小于等于$ans$的数字,那么就代表$ans$就是当前区间的神秘数,查询$sum$可以主席树维护...
代码:
#include<algorithm> #include<iostream> #include<cstring> #include<cstdio> //by NeighThorn #include<map> using namespace std; const int maxn=100000+5,maxm=7000000+5; int n,m,id,tot,a[maxn],mp[maxn],ls[maxm],rs[maxm],val[maxn],root[maxn]; long long ans,tmp,sum[maxm]; inline bool cmp(int x,int y){ return a[x]<a[y]; } inline void change(int l,int r,int x,int &y,int pos,int val){ y=++tot;sum[y]=sum[x]+val; if(l==r) return; int mid=(l+r)>>1;ls[y]=ls[x],rs[y]=rs[x]; if(pos<=mid) change(l,mid,ls[x],ls[y],pos,val); else change(mid+1,r,rs[x],rs[y],pos,val); } inline long long query(int l,int r,int y,int L,int R){ if(l==L&&r==R) return sum[y]; int mid=(l+r)>>1; if(R<=mid) return query(l,mid,ls[y],L,R); else if(L>mid) return query(mid+1,r,rs[y],L,R); else return query(l,mid,ls[y],L,mid)+query(mid+1,r,rs[y],mid+1,R); } signed main(void){ scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&a[i]),mp[i]=i,val[i]=a[i]; sort(mp+1,mp+n+1,cmp);sort(val+1,val+n+1); for(int i=1;i<=n;i++) change(1,n,root[i-1],root[i],mp[i],a[mp[i]]); scanf("%d",&m); for(int i=1,l,r;i<=m;i++){ scanf("%d%d",&l,&r);ans=1; while(9524){ id=upper_bound(val+1,val+n+1,ans)-val-1; tmp=query(1,n,root[id],l,r); if(tmp<ans) break; else ans=tmp+1; } printf("%lld\n",ans); } return 0; }
By NeighThorn