5255 -- 【FJOI2016】神秘數
5255 -- 【FJOI2016】神秘數
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
Sample Output
2
4
8
8
8
思路有點巧妙啊。
復雜度分析題。
顯然,答案就是第一個\(i>sum[i]\)的位置,其中\(sum[i]\)表示所有權值\(\leq i\) 的元素之和。
我們考慮"暴力"。假設現在能連續組成\([1,now]\)之間的任意值,那麽我們考慮有沒有權值為\(now\)的元素:如果沒有,答案就是\(now+1\);否則我們的值域至少擴大了\(a_{now+1}\)(這裏\(a_{now+1}\)表示所有的權值為\(now+1\)的元素和)。然後我們就將新增的值域\([now+1,now+1+a_{now+1}]\)中的所有元素加上,就這樣一直叠代下去。
因為每次叠代值域至少擴大一倍,所以最多叠代\(log_2(1e9)\)次。
代碼:
#include<bits/stdc++.h> #define ll long long #define N 100005 using namespace std; inline int Get() {int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}while('0'<=ch&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}return x*f;} int n,m; int rt[N]; int ls[N*50],rs[N*50]; int sum[N*50]; const int lx=1,rx=1e9; int tot; void Insert(int &v,int old,int lx,int rx,int p) { v=++tot; ls[v]=ls[old]; rs[v]=rs[old]; sum[v]=sum[old]+p; if(lx==rx) return ; int mid=lx+rx>>1; if(p<=mid) Insert(ls[v],ls[old],lx,mid,p); else Insert(rs[v],rs[old],mid+1,rx,p); } int query(int a,int b,int lx,int rx,int l,int r) { if(lx>r||rx<l) return 0; if(l<=lx&&rx<=r) return sum[b]-sum[a]; int mid=lx+rx>>1; return query(ls[a],ls[b],lx,mid,l,r)+query(rs[a],rs[b],mid+1,rx,l,r); } int main() { n=Get(); for(int i=1;i<=n;i++) { int a=Get(); Insert(rt[i],rt[i-1],lx,rx,a); } m=Get(); int l,r; while(m--) { l=Get(),r=Get(); int now=0,last=0,tem; while(1) { tem=query(rt[l-1],rt[r],lx,rx,last+1,now+1); if(!tem) break; last=now+1; now+=tem; } cout<<now+1<<"\n"; } return 0; }
5255 -- 【FJOI2016】神秘數