4408: [Fjoi 2016]神秘数
Time Limit: 10 Sec Memory Limit: 128 MB
Submit: 475 Solved: 287
[Submit][Status][Discuss]
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
1 2 4 9 10
5
1 1
1 2
1 3
1 4
1 5
Sample Output
4
8
8
8
HINT
对于100%的数据点,n,m <= 100000,∑a[i] <= 10^9
Source
Solution
这题的思路还是很妙的。
我们假设前面选的数能取到的最大值为k,即[1,k]中的所有数都能取到,现在加入了一个数x。
若x <= k+1,则能取到的最大值为k+x,神秘数为k+x+1
若x > k+1,则能取到的最大值仍为k,神秘数为k+1
对于一个询问(l,r),设当前神秘数为k,若Σai >= k(ai <= k && i∈[l,r]),则说明k仍能增大,否则,神秘数即为k。
而求前缀和,可以用可持久化线段树来解决。
每次寻找,k至少增加一倍,每次求前缀和是logn,总的时间复杂度是O(Q*log1e92)
Code
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <algorithm> using namespace std; #define REP(i, a, b) for (int i = (a), i##_end_ = (b); i <= i##_end_; ++i)
typedef long long LL;
const int maxn = 1e5+;
int n, a[maxn];
struct Node
{
LL sum; int ls, rs;
Node() { sum = ls = rs = ; }
}t[maxn*];
int rt[maxn], t_cnt, mx_a; void pushup(Node &x) { x.sum = t[x.ls].sum+t[x.rs].sum; } void update(int now_rt, int las_rt, int l, int r, int x)
{
if (l == r)
{
t[now_rt].sum = t[las_rt].sum+x;
return ;
}
int mid = (l+r)>>;
if (x <= mid)
{
t[now_rt].rs = t[las_rt].rs, t[now_rt].ls = ++t_cnt;
update(t[now_rt].ls, t[las_rt].ls, l, mid, x);
}
else
{
t[now_rt].ls = t[las_rt].ls, t[now_rt].rs = ++t_cnt;
update(t[now_rt].rs, t[las_rt].rs, mid+, r, x);
}
pushup(t[now_rt]);
} int query(int rt_1, int rt_2, int l, int r, int x)
{
if (l > x) return ;
if (r <= x) return t[rt_2].sum-t[rt_1].sum;
int mid = (l+r)>>;
int ret = query(t[rt_1].ls, t[rt_2].ls, l, mid, x);
if (x > mid) ret += query(t[rt_1].rs, t[rt_2].rs, mid+, r, x);
return ret;
} int calc(int l, int r)
{
int ret = , temp = ;
while ((temp = query(rt[l-], rt[r], , mx_a, ret)) >= ret) ret = temp+;
return ret;
} int main()
{
scanf("%d", &n);
REP(i, , n) scanf("%d", &a[i]), mx_a = max(mx_a, a[i]);
REP(i, , n) update(rt[i] = ++t_cnt, rt[i-], , mx_a, a[i]);
int Q, l, r;
scanf("%d", &Q);
while (Q --)
{
scanf("%d %d", &l, &r);
printf("%d\n", calc(l, r));
}
return ;
}