[BZOJ4408&&BZOJ4299][FJOI2016 && Codechef]神秘数&&FRBSUM(主席树)
4299: Codechef FRBSUM
Time Limit: 10 Sec Memory Limit: 128 MB
Submit: 550 Solved: 351
[Submit][Status][Discuss]Description
数集S的ForbiddenSum定义为无法用S的某个子集(可以为空)的和表示的最小的非负整数。例如,S={1,1,3,7},则它的子集和中包含0(S’=∅),1(S’={1}),2(S’={1,1}),3(S’={3}),4(S’={1,3}),5(S' = {1, 1, 3}),但是它无法得到6。因此S的ForbiddenSum为6。给定一个序列A,你的任务是回答该数列的一些子区间所形成的数集的ForbiddenSum是多少。Input
输入数据的第一行包含一个整数N,表示序列的长度。接下来一行包含N个数,表示给定的序列A(从1标号)。接下来一行包含一个整数M,表示询问的组数。接下来M行,每行一对整数,表示一组询问。Output
对于每组询问,输出一行表示对应的答案。Sample Input
5
1 2 4 9 10
5
1 1
1 2
1 3
1 4
1 5Sample Output
2
4
8
8
8HINT
对于100%的数据,1≤N,M≤100000,1≤A_i≤10^9,1≤A_1+A_2+…+A_N≤10^9。
Source
[Fjoi 2016]神秘数
时间限制:10s 空间限制:128MB
题目描述
一个可重复数字集合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]所构成的可重复数字集合的神秘数。
输入格式
第一行一个整数n,表示数字个数。
第二行n个整数,从1编号。
第三行一个整数m,表示询问个数。
以下m行,每行一对整数l,r,表示一个询问。
输出格式
对于每个询问,输出一行对应的答案。
样例输入
5 1 2 4 9 10 5 1 1 1 2 1 3 1 4 1 5
样例输出
2 4 8 8 8
提示
对于100%的数据点,n,m <= 100000,∑a[i] <= 10^9
题目来源
鸣谢yyh上传
这两题实际上是双倍经验,但我做了两遍且两遍都不会做。。
主席树非常巧妙的应用,对于一个集合S,如果S中的数可以凑出mx,那么如果向集合S中新添加一个数x,若x<=mx+1,那么加入x后S可以凑出0~mx+x的所有数,但如果x>mx+1,则x对扩大可凑出的数的范围没有作用。
这样这题就可做了,设当前已知的一定能凑出来的范围是0~mx(mx初值为0),用权值主席树快速求出区间[l,r]中所有不大于mx+1的数的和作为新的mx,如果发现mx没有增大说明mx+1就是不可凑出的数。
复杂度O(nlog^2n)
BZOJ4299:
1 #include<cstdio>
2 #include<algorithm>
3 #define rep(i,l,r) for (int i=l; i<=r; i++)
4 using namespace std;
5
6 const int N=100010;
7 int n,m,nd,l,r,mx,lst,a[N],rt[N],L[N*40],R[N*40],tot[N*40];
8
9 void ins(int x,int &y,int l,int r,int k){
10 y=++nd; L[y]=L[x]; R[y]=R[x]; tot[y]=tot[x]+k;
11 if (l==r) return; int mid=(l+r)>>1;
12 if (k<=mid) ins(L[x],L[y],l,mid,k); else ins(R[x],R[y],mid+1,r,k);
13 }
14
15 int que(int x,int y,int l,int r,int k){
16 if (l==r) return tot[y]-tot[x];
17 int mid=(l+r)>>1;
18 if (k<=mid) return que(L[x],L[y],l,mid,k);
19 else return tot[L[y]]-tot[L[x]]+que(R[x],R[y],mid+1,r,k);
20 }
21
22 int main(){
23 scanf("%d",&n);
24 rep(i,1,n) scanf("%d",a+i),ins(rt[i-1],rt[i],1,1e9,a[i]);
25 for (scanf("%d",&m); m--; ){
26 scanf("%d%d",&l,&r); mx=lst=0;
27 while (1){
28 mx=que(rt[l-1],rt[r],1,1e9,mx+1);
29 if (lst==mx) break; lst=mx;
30 }
31 printf("%d\n",lst+1);
32 }
33 return 0;
34 }
BZOJ4408:
1 #include<cstdio>
2 #include<algorithm>
3 #define rep(i,l,r) for (int i=l; i<=r; i++)
4 using namespace std;
5
6 const int N=100100,M=2000100;
7 int n,m,l,r,nd,tot,t,ans,a[N],num[N],rt[N],sm[M],ls[M],rs[M];
8
9 int find(int x){
10 int L=1,R=tot+1;
11 while (L+1<R){
12 int mid=(L+R)>>1;
13 if (x<num[mid]) R=mid; else L=mid;
14 }
15 return L;
16 }
17
18 void ins(int y,int &x,int L,int R,int pos,int k){
19 sm[x=++nd]=sm[y]+k; ls[x]=ls[y]; rs[x]=rs[y];
20 if (L==R) return;
21 int mid=(L+R)>>1;
22 if (pos<=mid) ins(ls[y],ls[x],L,mid,pos,k); else ins(rs[y],rs[x],mid+1,R,pos,k);
23 }
24
25 int que(int l,int r,int k){
26 int L=1,R=tot,mid,ans=0; l=rt[l-1]; r=rt[r];
27 while (L<R){
28 mid=(L+R)>>1;
29 if (k<=mid) R=mid,l=ls[l],r=ls[r];
30 else L=mid+1,ans+=sm[ls[r]]-sm[ls[l]],l=rs[l],r=rs[r];
31 }
32 return ans+sm[r]-sm[l];
33 }
34
35 int main(){
36 freopen("bzoj4408.in","r",stdin);
37 freopen("bzoj4408.out","w",stdout);
38 scanf("%d",&n);
39 rep(i,1,n) scanf("%d",&a[i]),num[i]=a[i]; n++;
40 sort(num+1,num+n+1); tot=unique(num+1,num+n+1)-num-1;
41 rep(i,1,n) a[i]=find(a[i]);
42 rep(i,1,n) ins(rt[i-1],rt[i],1,tot,a[i],num[a[i]]);
43 for (scanf("%d",&m); m--; ){
44 scanf("%d%d",&l,&r);
45 for (ans=1; ; ans=t+1){
46 t=que(l,r,find(ans));
47 if (t<ans) break;
48 }
49 printf("%d\n",ans);
50 }
51 return 0;
52 }