1. 程式人生 > >[Luogu P4587] [BZOJ 4408] [FJOI2016]神祕數

[Luogu P4587] [BZOJ 4408] [FJOI2016]神祕數

洛谷傳送門

題目描述

一個可重複數字集合SS的神祕數定義為最小的不能被SS的子集的和表示的正整數。例如S={1,1,1,4,13}S=\{1,1,1,4,13\}

1=11 = 1

2=1+12 = 1+1

3=1+1+13 = 1+1+1

4=44 = 4

5=4+15 = 4+1

6=4+1+16 = 4+1+1

7=4+1+1+17 = 4+1+1+1

88無法表示為集合SS的子集的和,故集合SS的神祕數為88

現給定nn個正整數a[1]...a[n]a[1]...a[n]

mm個詢問,每次詢問給定一個區間[l,r][l,r],求由a[l],a[l+1],,a[r]a[l],a[l+1],…,a[r]所構成的可重複數字集合的神祕數。

輸入輸出格式

輸入格式:

第一行一個整數nn,表示數字個數。

第二行nn個整數,從11編號。

第三行一個整數mm,表示詢問個數。

以下mm行,每行一對整數l,rl,r,表示一個詢問。

輸出格式:

對於每個詢問,輸出一行對應的答案。

輸入輸出樣例

輸入樣例#1:

5
1 2 4 9 10
5
1 1
1 2
1 3
1 4
1 5

輸出樣例#1:

2
4
8
8
8

說明

對於100%的資料點,$n,m \le 10000000,∑a[i] \le 10^9$

解題分析

這道題的思路來自於一個很顯然的結論: 從11開始遞推, 如果ansi=1ansnum[i]×ians\le \sum_{i=1}^{ans}num[i]\times i的數湊出來, 那麼i=1ansnum[i]×i\sum_{i=1}^{ans}num[i]\times i也能湊出來。 這樣搞我們用主席樹維護, 每次ansans至少增大一倍, 總複雜度就是O(Nlog2(N))O(Nlog^2(N))

N))的。

程式碼如下:

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <cctype>
#include <algorithm>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define MX 100050
template <class T>
IN void in(T &x)
{
	x = 0; R char c = gc;
	for (; !isdigit(c); c = gc);
	for (;  isdigit(c); c = gc)
	x = (x << 1) + (x << 3) + c - 48;
}
int dot, q, cnt, dif;
struct Node {int sum, son[2];} tree[MX * 40];
int root[MX], buf[MX], dat[MX];
namespace SGT
{
	#define ls tree[now].son[0]
	#define rs tree[now].son[1]
	#define pls tree[pre].son[0]
	#define prs tree[pre].son[1]
	void insert(R int pre, int &now, R int lef, R int rig, R int tar, R int val)
	{
		now = ++cnt; tree[now] = tree[pre]; tree[now].sum += val;
		if(lef == rig) return;
		int mid = lef + rig >> 1;
		if(tar <= mid) insert(pls, ls, lef, mid, tar, val);
		else insert(prs, rs, mid + 1, rig, tar, val);
	}
	int query(R int pre, R int now, R int lef, R int rig, R int bd)
	{
		if(rig <= bd) return tree[now].sum - tree[pre].sum;
		int mid = lef + rig >> 1;
		int ret = query(pls, ls, lef, mid, bd);
		if(bd > mid) ret += query(prs, rs, mid + 1, rig, bd);
		return ret;
	}
}
IN int getid(R int now) {int k = std::lower_bound(buf + 1, buf + 1 + dif, now) - buf; if(buf[k] > now) k--; return k;}
int main(void)
{
	int a, b, ans, pos, sum;
	in(dot);
	for (R int i = 1; i <= dot; ++i) in(dat[i]), buf[i] = dat[i];
	std::sort(buf + 1, buf + 1 + dot); dif = std::unique(buf + 1, buf + 1 + dot) - buf - 1;
	for (R int i = 1; i <= dot; ++i) SGT::insert(root[i - 1], root[i], 1, dif, getid(dat[i]), dat[i]);
	in(q);
	W(q--)
	{
		in(a), in(b);
		ans = 1;
		W(233)
		{
			sum = SGT::query(root[a - 1], root[b], 1, dif, getid(ans));
			if(sum >= ans) ans = sum + 1;
			else break;
		}
		printf("%d\n", ans);
	}
}