bzoj 4299 - Codechef FRBSUM

mac2024-04-12  31

4299: Codechef FRBSUM

Time Limit: 10 Sec Memory Limit: 128 MB Submit: 756 Solved: 486 [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 5 Sample Output 2

4

8

8

8 HINT 对于100%的数据,1≤N,M≤100000,1≤A_i≤109,1≤A_1+A_2+…+A_N≤109。


我们对于每一个区间试想: 如果当前我们可以凑出 [ 0 , x ],那么小于等于x+1的值,都会对答案造成贡献值。所以每次我们把答案小于x+1的所有值算出来,如果大于x,就更新右区间,否则已经无法更新了,直接退出即可。

然后每次算时,类似于斐波那契,速度是log的。

然后每个区间的权值,用主席树维护一下即可。


AC代码:

#pragma GCC optimize(2) #include<bits/stdc++.h> #define int long long using namespace std; const int N=1e5+10; int n,a[N],b[N],m,q,rt[N],cnt; struct node{ int l,r,sum; }t[N*20]; void change(int l,int r,int &x,int y,int k){ x=++cnt; t[x]=t[y]; t[x].sum+=b[k]; if(l==r) return ; int mid=l+r>>1; if(k<=mid) change(l,mid,t[x].l,t[y].l,k); else change(mid+1,r,t[x].r,t[y].r,k); } int ask(int l,int r,int x,int y,int k){ if(r==l) return (t[x].sum-t[y].sum)*(k>=l); int mid=l+r>>1; if(k<=mid) return ask(l,mid,t[x].l,t[y].l,k); else return t[t[x].l].sum-t[t[y].l].sum+ask(mid+1,r,t[x].r,t[y].r,k); } signed main(){ cin>>n; for(int i=1;i<=n;i++) scanf("%lld",&a[i]),b[i]=a[i]; sort(b+1,b+1+n); m=unique(b+1,b+1+n)-b-1; for(int i=1;i<=n;i++) a[i]=lower_bound(b+1,b+1+m,a[i])-b; for(int i=1;i<=n;i++) change(1,m,rt[i],rt[i-1],a[i]); cin>>q; while(q--){ int x,y,res=0; scanf("%lld %lld",&x,&y); while(1){ int id=lower_bound(b+1,b+1+m,res+1)-b; if(b[id]!=res+1) id--; int t=ask(1,m,rt[y],rt[x-1],id); if(t<=res) break; else res=t; } printf("%lld\n",res+1); } return 0; }
最新回复(0)