bzoj4299: Codechef FRBSUM主席树

bzoj4299: Codechef FRBSUM

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。

分析

一道双倍经验题。
考虑暴力。
假设你可以凑出[0,R]的数,剩下若干个数可以选择,那么你只能在[0,R]中找出一个数x,扩展到[0,R+x]
也就是说如果我凑到了[0,R]的数,意味着所有[0,R]的数都可以对答案有贡献。
那么更简单地,可以直接用[0,R]中所有数的和sum更新R,如果小于R就没法更新了。
用主席树模拟这个过程就行了。
复杂度分析的话,假设连续更新了两次 [ 0 , x 1 ] > [ 0 , x 2 ] > [ 0 , x 3 ]
如果说 [ 0 , x 2 ] 可以扩展,意味着一定存在一个 [ x 1 , x 2 ] 的数。
否则的话 s u m [ 0 , x 2 ] = s u m [ 0 , x 1 ] 矛盾。
也就是说每两次更新必定倍增。
所以复杂度是log
加上主席树一个log
中复杂度 O ( n l o g 2 n )

代码

#include<cstdio>
#include<algorithm>
const int N = 1e5 + 10, T = 3e6 + 10;
int ri() {
    char ch = getchar(); int x = 0;
    for(;ch < '0' || ch > '9'; ch = getchar()) ;
    for(;ch >= '0' && ch <= '9'; ch = getchar()) x = (x << 1) + (x << 3) - '0' + ch;
    return x;
}
int ls[T], rs[T], r[N], sz;
long long s[T];
void Ins(int &np, int lp, int L, int R, int v) {
    s[np = ++sz] = s[lp] + v;
    if(L == R) return ; int m = L + R >> 1;
    if(v <= m) Ins(ls[np], ls[lp], L, m, v), rs[np] = rs[lp];
    else Ins(rs[np], rs[lp], m + 1, R, v), ls[np] = ls[lp];
}
long long Que(int lt, int rt, int L, int R, int v) {
    if(v == R) return s[rt] - s[lt];
    int m = L + R >> 1; long long ans = 0;
    if(v <= m) return Que(ls[lt], ls[rt], L, m, v);
    else return s[ls[rt]] - s[ls[lt]] + Que(rs[lt], rs[rt], m + 1, R, v);
}
int main() {
    int n = ri();
    for(int i = 1;i <= n; ++i) Ins(r[i], r[i - 1], 1, N, ri());
    for(int m = ri();m--;) {
        int L = ri(), R = ri(); long long pr = -1, cr = 0;
        for(;pr != cr;) pr = cr, cr = Que(r[L - 1], r[R], 1, N, std::min(cr + 1, (long long)N));
        printf("%lld\n", cr + 1);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/lvzelong2014/article/details/80784775