P4587 [FJOI2016]神秘数(主席树)

题意:给出1e5个数 查询l,r区间内第一个不能被表示的数

   比如1,2,4可以用子集的和表示出[1,7] 所以第一个不能被表示的是8

题解:先考虑暴力的做法 把这个区间内的数字按从小到大排序后

   从前往后扫 当前能表示出[1,x] 假设第i个数字y-1<=x 那么就可以表示[1,x+y]

   如果y > x + 1那么第一个不能表示出的数字就是x+1

  

   我们根据这个性质来想 假如当前区间能表示出[1,x] 我们计算这个区间内所有比x小的数的和tmp

   如果tmp>x 那么我们显然可以表示出[1,tmp] 反之x+1就是答案 直接退出就好

   然后就用主席树来做求和这个东西

#include <bits/stdc++.h>
using namespace std;
const int INF = 1e9 + 5;
const int MAXN = 1e5 + 5;
int n, cnt;
int sum[MAXN << 5];
int ls[MAXN << 5], rs[MAXN << 5];
int t[MAXN];

int inser(int o, int l, int r, int pos, int val) {
    int rt = ++cnt;
    ls[rt] = ls[o], rs[rt] = rs[o], sum[rt] = sum[o] + val;
    int m = l + r >> 1;
    if(l < r)
    if(pos <= m)  ls[rt] = inser(ls[o], l, m, pos, val);
    else rs[rt] = inser(rs[o], m + 1, r, pos, val);
    return rt;
}

int query(int x, int y, int l, int r, int ql, int qr) {
    if(ql <= l && qr >= r) return sum[y] - sum[x];
    int m = l + r >> 1;
    int res = 0;
    if(ql <= m) res += query(ls[x], ls[y], l, m, ql, qr);
    if(qr > m) res += query(rs[x], rs[y], m + 1, r, ql, qr);
    return res;
}

int main() {
    cnt = 0;
    scanf("%d", &n);
    for(int i = 1; i <= n; i++) {
        int x; scanf("%d", &x);
        t[i] = inser(t[i - 1], 1, INF, x, x);
    }

    int T;
    scanf("%d", &T);
    while(T--) {
        int l, r;
        scanf("%d%d", &l, &r);
        int ans = 1;
        while(1) {
            int tmp = query(t[l - 1], t[r], 1, INF, 1, ans);
            if(tmp >= ans) ans = tmp + 1;
            else break;
        }
        printf("%d\n", ans);
    }
    return 0;
}
View Code

猜你喜欢

转载自www.cnblogs.com/lwqq3/p/11897977.html