LG 題解 P4587 [FJOI2016]神祕數
阿新 • • 發佈:2021-08-02
我不會忘記,一直,都留在我的心間。
,下一個新增進去的數為 \(a_i\)。
內的所有數放進去了,那麼我們設 \(sum = \sum a_i [lst + 1 \le a_i \le now + 1]\),即對值域在 \([lst+1, now+1]\) 內的數求和,把他們一起放進去。
那麼我們已經用過的數的區間為 \([1,now + 1]\),可以表示出來的數的區間為 \([1,now+sum]\)。
注:上面這塊的區間均指值域區間而不是下標區間,注意區分。 。
思路挺仙的一題,不過有了思路就是個主席樹的板子了
前置芝士
- 主席樹
Description
一個可重複數字集合 \(S\) 的神祕數定義為最小的不能被 \(S\) 的子集的和表示的正整數。
現在給你一個長度為 \(n\) 的序列 \(a\) ,每次詢問一個 \([l,r]\),問 \([l,r]\) 區間的陣列成的集合的神祕數。
\(n,m \le 10^5, \sum a_i \le 10^9\)。
Solution
考慮沒有區間查詢時怎麼做。
首先肯定要考慮有沒有 \(1\) ,沒有直接輸出 \(1\)。
先對 \(a\) 序列進行排序。
設當前我們已經表示出了 \([1,x]\)
- 如果 \(a_i \le x+1\),填加後能表示出來的區間為 \([1,x] \cup [1 + a_i, x + a_i] = [1, x + a_i]\)。
- 如果 \(a_i > x + 1\),那麼 \(x+1\) 就表示不出來,所以答案為 \(x+1\)。
這樣一次詢問的複雜度是 \(O(n)\) 的。
考慮優化這個新增過程。
假設用值域在 \([1,lst]\) 區間內的數可以表示出來的區間為 \([1,now]\)。
那麼我們再把值域在 \([1,now+1]\) 區間內的任何數放進去都是合法的。
因為我們已經把值域在 \([1,lst]\)
那麼我們已經用過的數的區間為 \([1,now + 1]\),可以表示出來的數的區間為 \([1,now+sum]\)。
注:上面這塊的區間均指值域區間而不是下標區間,注意區分。
上面這個值域的擴充套件過程類似於斐波那契數列,所以說複雜度是 \(\log (\sum a_i)\) 的。
查詢一段區間內一個值域區間的和就是主席樹板子做的事了。單次求和為 $\log $ 複雜度。
所以總複雜度為 \(O(n \log n \log (\sum a_i))\)
Code
/*
Work by: Suzt_ilymics
Knowledge: ??
Time: O(??)
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#include<string>
#define int long long
#define orz cout<<"lkp AK IOI!"<<endl
using namespace std;
const int MAXN = 1e5+5;
const int INF = 1e9+7;
const int mod = 1e9+7;
const int Max = 1e9;
int n, m;
int root[MAXN];
int read(){
int s = 0, f = 0;
char ch = getchar();
while(!isdigit(ch)) f |= (ch == '-'), ch = getchar();
while(isdigit(ch)) s = (s << 1) + (s << 3) + ch - '0' , ch = getchar();
return f ? -s : s;
}
namespace Hjt {
#define ls lson[now_]
#define rs rson[now_]
int lson[MAXN << 5], rson[MAXN << 5], val[MAXN << 5], node_num = 0;
void Insert(int &now_, int pre_, int l, int r, int pos, int val_) {
now_ = ++ node_num;
val[now_] = val[pre_] + val_;
ls = lson[pre_], rs = rson[pre_];
if(l == r) return ;
int mid = (l + r) >> 1;
if(pos <= mid) Insert(ls, lson[pre_], l, mid, pos, val_);
else Insert(rs, rson[pre_], mid + 1, r, pos, val_);
}
int Query(int now_, int pre_, int l, int r, int L, int R) {
if(!(val[now_] - val[pre_])) return 0;
if(L <= l && r <= R) return val[now_] - val[pre_];
int mid = (l + r) >> 1, res = 0;
if(mid >= L) res += Query(ls, lson[pre_], l, mid, L, R);
if(mid < R) res += Query(rs, rson[pre_], mid + 1, r, L, R);
return res;
}
}
signed main()
{
n = read();
for(int i = 1, x; i <= n; ++i) {
x = read();
Hjt::Insert(root[i], root[i - 1], 1, Max, x, x);
}
m = read();
for(int i = 1, l, r; i <= m; ++i) {
l = read(), r = read();
int lst = 0, now = 0;
while(1 + 2 == 3) {
int tmp = Hjt::Query(root[r], root[l - 1], 1, Max, lst + 1, now + 1);
// cout<<"tmp: "<<tmp<<"\n";
if(tmp) lst = now + 1, now += tmp;
else break;
}
printf("%lld\n", now + 1);
}
return 0;
}