571 字
3 分钟
BZOJ 4299 Codechef FRBSUM
2017-08-09

Description#

数集S的ForbiddenSum定义为无法用S的某个子集(可以为空)的和表示的最小的非负整数。 例如,S={1,1,3,7},则它的子集和中包含0(S’=∅),1(S’={1}),2(S’={1,1}),3(S’={3}),4(S’={1,3}),5(S’ = {1, 1, 3}),但是它无法得到6。因此S的ForbiddenSum为6。 给定一个序列A,你的任务是回答该数列的一些子区间所形成的数集的ForbiddenSum是多少。

Input#

输入数据的第一行包含一个整数N,表示序列的长度。 接下来一行包含N个数,表示给定的序列A(从1标号)。 接下来一行包含一个整数M,表示询问的组数。 接下来M行,每行一对整数,表示一组询问。

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

HINT#

对于100%的数据,1≤N,M≤100000,1≤A_i≤10^9,1≤A_1+A_2+…+A_N≤10^9。

题解#

可以看出

如果一个区间能组出1~n的数而且有一个数小于等于n+1
则这个区间一定能构造出 n+1

然后就可以了

用主席树维护一下就可以了

复习一下主席树

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int INF = 1e9;
struct Seg_Node
{
    int l, r;
    Seg_Node *ch[2];
    int Sum;
} * root[100005], *null;
int a[100005];
Seg_Node *NewSeg_Node()
{
    Seg_Node *S = new Seg_Node();
    S->ch[0] = S->ch[1] = null;
    return S;
}
void copy(Seg_Node *&a, Seg_Node *b)
{
    if (b == null)
        a = null;
    else
        a = NewSeg_Node(), *a = *b;
}
void Update(int v, int l, int r, Seg_Node *&rt1, Seg_Node *rt2)
{
    copy(rt1, rt2);
    if(rt1==null)
        rt1=NewSeg_Node();
    rt1->Sum += v;
    rt1->l = l, rt1->r = r;
    if (l == r)
        return;
    int m = l + r >> 1;
    if (v <= m)
    {
        Update(v, l, m, rt1->ch[0], rt2->ch[0]);
    }
    else
    {
        Update(v, m + 1, r, rt1->ch[1], rt2->ch[1]);
    }
}
int Query(int R, int l, int r, Seg_Node *rt1, Seg_Node *rt2)
{
    if (R >= r)
        return rt1->Sum - rt2->Sum;
    int m = l + r >> 1;
    if (R <= m)
        return Query(R, l, m, rt1->ch[0], rt2->ch[0]);
    return Query(R, l, m, rt1->ch[0], rt2->ch[0]) + Query(R, m + 1, r, rt1->ch[1], rt2->ch[1]);
}
int main()
{
    null = new Seg_Node();
    null->ch[0] = null->ch[1] = null;
    root[0] = null;
    int n;
    scanf("%d", &n);
    for (int i = 1; i <= n; i++)
        scanf("%d", &a[i]);
    for (int i = 1; i <= n; i++)
    {
        Update(a[i], 1, 1e9, root[i], root[i - 1]);
    }
    int m;
    scanf("%d", &m);
    int a, b, ans;
    while (m--)
    {
        scanf("%d%d", &a, &b);
        int res = 1;
        ans = min(INF, Query(res, 1, 1e9, root[b], root[a - 1]));
        while (ans >= res && res < 1e9)
        {
            res = ans + 1;
            ans = min(INF, Query(res, 1, 1e9, root[b], root[a - 1]));
        }
        printf("%d\n", ans + 1);
    }
}
BZOJ 4299 Codechef FRBSUM
https://www.nekomio.com/posts/83/
作者
NekoMio
发布于
2017-08-09
许可协议
CC BY-NC-SA 4.0