【bzoj4408】[Fjoi 2016]神秘数 主席树
題目描述
一個可重復(fù)數(shù)字集合S的神秘數(shù)定義為最小的不能被S的子集的和表示的正整數(shù)。例如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的神秘數(shù)為8。
現(xiàn)給定n個正整數(shù)a[1]..a[n],m個詢問,每次詢問給定一個區(qū)間[l,r](l<=r),求由a[l],a[l+1],…,a[r]所構(gòu)成的可重復(fù)數(shù)字集合的神秘數(shù)。
輸入
第一行一個整數(shù)n,表示數(shù)字個數(shù)。
第二行n個整數(shù),從1編號。
第三行一個整數(shù)m,表示詢問個數(shù)。
以下m行,每行一對整數(shù)l,r,表示一個詢問。
輸出
對于每個詢問,輸出一行對應(yīng)的答案。
樣例輸入
5
1 2 4 9 10
5
1 1
1 2
1 3
1 4
1 5
樣例輸出
2
4
8
8
8
題解
主席樹的一道神題
我們先想暴力怎么做:把一段區(qū)間的數(shù)取出來,排個序,從小到大選擇。如果$a1$~$a_{i-1}$能夠表示$1~x$,此時加入$a_i$,如果$a_i\le x+1$,那么就可以表示$x+a_i$,否則x就是答案。
試著優(yōu)化一下這個過程:設(shè)$a_{i-1}=k$,$a_i=y$,1~i-1的神秘數(shù)為ans=x+1,那么顯然$ans=\sum\limits_{t=1}^{i-1}a_t$。此時如果存在k+1~ans的數(shù)就可以更新ans。更具體地,如果k+1~ans內(nèi)的數(shù)的和為s,那么ans+=s;而ans為1~k的數(shù)的和+1,故ans的新值應(yīng)該賦為1~ans的數(shù)的和。
說了這么多廢話有什么用?我們可以發(fā)現(xiàn)每次ans的增量都大于等于前一次的ans,所以這個過程的時間復(fù)雜度應(yīng)該為$O(\log a)$。
而事實(shí)上我們并不能把區(qū)間拿出來排序,所以需要使用數(shù)據(jù)結(jié)構(gòu),上一個主席樹就好了。
時間復(fù)雜度為$O(n\log^2n)$
#include <cstdio> #include <algorithm> #define N 100010 using namespace std; int v[N] , a[N] , root[N] , ls[N << 5] , rs[N << 5] , sum[N << 5] , tot; void insert(int p , int l , int r , int x , int &y) {y = ++tot , sum[y] = sum[x] + a[p];if(l == r) return;int mid = (l + r) >> 1;if(p <= mid) rs[y] = rs[x] , insert(p , l , mid , ls[x] , ls[y]);else ls[y] = ls[x] , insert(p , mid + 1 , r , rs[x] , rs[y]); } int query(int p , int l , int r , int x , int y) {if(r <= p) return sum[y] - sum[x];int mid = (l + r) >> 1;if(p <= mid) return query(p , l , mid , ls[x] , ls[y]);else return query(p , mid + 1 , r , rs[x] , rs[y]) + sum[ls[y]] - sum[ls[x]]; } int main() {int n , m , i , x , y , ans , tmp;scanf("%d" , &n);for(i = 1 ; i <= n ; i ++ ) scanf("%d" , &a[i]) , v[i] = a[i];sort(a + 1 , a + n + 1);for(i = 1 ; i <= n ; i ++ ) v[i] = lower_bound(a + 1 , a + n + 1 , v[i]) - a;for(i = 1 ; i <= n ; i ++ ) insert(v[i] , 1 , n , root[i - 1] , root[i]);a[n + 1] = 1 << 30;scanf("%d" , &m);while(m -- ){scanf("%d%d" , &x , &y) , ans = 1;while((tmp = query(upper_bound(a + 1 , a + n + 2 , ans) - a - 1 , 1 , n , root[x - 1] , root[y])) >= ans)ans = tmp + 1;printf("%d\n" , ans);}return 0; }?
?
轉(zhuǎn)載于:https://www.cnblogs.com/GXZlegend/p/7115254.html
總結(jié)
以上是生活随笔為你收集整理的【bzoj4408】[Fjoi 2016]神秘数 主席树的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 快学Scala习题解答—第十章 特质
- 下一篇: 为什么使用hibernate