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
题意极为简洁。。。。
可以发现这个题需要分析一些性质来判断一个数是否可以被凑出来。一个数被凑出来的时候一定不会用到大于它的数字,只可能会用到小于它的数字。
问题看起来很无序,那么找到一个考虑的顺序,把询问区间内所有的数字从小到大开始考虑。首先看有没有1,如果有,那么1就可以凑出来,同时可以发现如果有x个1,那么1~x都可以凑出来。那么考虑1~x范围内的所有数字,你可发现任何一个非1的数字都可以使我们可以连续表达的数字区间增大了(脑补一个移动的窗口),1~x区间内能表示出的最大数字就是 x+(区间内小于等于x的最大值)。仔细一想这正是一个同构子问题!
每一次都这样做,我们最多会扩大几次考虑范围呢?不难发现只要还有答案成立,这一次扩大之后的考虑范围至少是上一次的两倍,所以最多也就log级别。
于是我们只需要维护区间内小于等于某个数字的和就可以了,开心上一发主席树板子。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<cstdlib> 5 #include<algorithm> 6 #include<cmath> 7 #include<queue> 8 #include<set> 9 #include<map> 10 #include<vector> 11 #include<cctype> 12 using namespace std; 13 const int MAXN=100005; 14 15 int N,M,AA[MAXN],rank[MAXN],tot; 16 struct persistable_segment_tree{ 17 static const int maxn=1900005; 18 static const int maxm=100005; 19 int root[maxm],np,lc[maxn],rc[maxn],sum[maxn]; 20 persistable_segment_tree(){ np=0; } 21 void pushup(int now){ sum[now]=sum[lc[now]]+sum[rc[now]]; } 22 void build(int &now,int L,int R){ 23 now=++np,lc[now]=rc[now]=sum[now]=0; 24 if(L==R) return; 25 int m=L+R>>1; 26 build(lc[now],L,m); build(rc[now],m+1,R); 27 } 28 int copynode(int now){ 29 np++,lc[np]=lc[now],rc[np]=rc[now],sum[np]=sum[now]; 30 return np; 31 } 32 void update(int &now,int L,int R,int pos,int v){ 33 now=copynode(now); 34 if(L==R){ sum[now]+=v; return; } 35 int m=L+R>>1; 36 if(pos<=m) update(lc[now],L,m,pos,v); 37 else update(rc[now],m+1,R,pos,v); 38 pushup(now); 39 } 40 int query(int xx,int yy,int L,int R,int A,int B){ 41 if(A<=L&&R<=B) return sum[yy]-sum[xx]; 42 int m=L+R>>1; 43 if(B<=m) return query(lc[xx],lc[yy],L,m,A,B); 44 if(A>m) return query(rc[xx],rc[yy],m+1,R,A,B); 45 return query(lc[xx],lc[yy],L,m,A,B)+query(rc[xx],rc[yy],m+1,R,A,B); 46 } 47 }st; 48 49 void data_in() 50 { 51 scanf("%d",&N); 52 for(int i=1;i<=N;i++) scanf("%d",&AA[i]); 53 scanf("%d",&M); 54 } 55 void work() 56 { 57 memcpy(rank,AA,sizeof(AA)); 58 sort(rank+1,rank+N+1); 59 tot=unique(rank+1,rank+N+1)-rank; 60 st.build(st.root[0],1,tot-1); 61 for(int i=1;i<=N;i++){ 62 st.root[i]=st.root[i-1]; 63 st.update(st.root[i],1,tot-1,lower_bound(rank+1,rank+tot,AA[i])-rank,AA[i]); 64 } 65 int l,r,pos,ans,tmp; 66 for(int i=1;i<=M;i++){ 67 scanf("%d%d",&l,&r); 68 pos=upper_bound(rank+1,rank+tot,1)-rank-1,ans=1; 69 while(pos){ 70 tmp=st.query(st.root[l-1],st.root[r],1,tot-1,1,pos); 71 if(tmp<ans) break; 72 ans=tmp+1; 73 if(pos==tot-1) break; 74 pos=upper_bound(rank+1,rank+tot,ans)-rank-1; 75 } 76 printf("%d ",ans); 77 } 78 } 79 int main() 80 { 81 data_in(); 82 work(); 83 return 0; 84 }