【FJOI2016】【BZOJ4408】神秘数

【题目链接】

【前置技能】

  • 主席树

【题解】

  • 首先先考虑一种时间复杂度为 O ( Q N l o g N ) 的暴力做法:将所有数按从小到大排序,考虑做到第 p o s 位前缀和为 S p o s ,并且前面的数能表示 [ 1 , S p o s ] 中的所有数,那么如果 a p o s + 1 S p o s + 1 ,则前 p o s + 1 个数可以表示 [ 1 , S p o s + 1 ] 中的所有数,否则,第一个不能被表示的神秘数就是 S p o s + 1
  • 那么我们考虑如何加速这个过程:若目前的前缀和为 S ,那么我们可以一次性将所有小于等于 S 的数求和,作为新的 S 重复上述过程。显然这个过程实质上和上面依次加入的过程是一样的,那么这么做是否要比暴力快呢?发现这么做最坏的情况是斐波那切数列,我们可以认为这是 O ( l o g a i ) 级别的。
  • 那么对于本题,要求区间询问,支持区间求小于等于某个数的和,我们就可以用主席树维护。注意数的范围打到了 10 9 ,建主席树的时候可以考虑离散化或者动态开节点。
  • 时间复杂度 O ( Q l o g N l o g a i )

【代码】

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define LL  long long
#define MAXN    100010
#define MAXLOG  30
using namespace std;
int N, w[MAXN], Q; 

template <typename T> void chkmin(T &x, T y){x = min(x, y);}
template <typename T> void chkmax(T &x, T y){x = max(x, y);}
template <typename T> void read(T &x){
    x = 0; int f = 1; char ch = getchar();
    while (!isdigit(ch)) {if (ch == '-') f = -1; ch = getchar();}
    while (isdigit(ch)) {x = x * 10 + ch - '0'; ch = getchar();}
    x *= f;
}

struct Segment_Tree{
    struct info{int ls, rs, sum;}a[MAXN * MAXLOG];
    int n, cnt, root[MAXN];
    void update(int &pos, int old, int l, int r, int p){
        pos = ++cnt, a[pos] = a[old], a[pos].sum += p;
        if (l == r) return;
        int mid = (l + r) >> 1;
        if (p <= mid) update(a[pos].ls, a[old].ls, l, mid, p);
        else update(a[pos].rs, a[old].rs, mid + 1, r, p);
    }
    void init(int x){
        n = x, cnt = 0;
        root[0] = ++cnt;
        for (int i = 1; i <= N; ++i)
            update(root[i], root[i - 1], 1, n, w[i]);
    }
    int query(int lp, int rp, int l, int r, int ll, int rr){
        if (l == ll && r == rr) return a[rp].sum - a[lp].sum;
        int mid = (l + r) >> 1;
        if (rr <= mid) return query(a[lp].ls, a[rp].ls, l, mid, ll, rr);
        else if (ll > mid) return query(a[lp].rs, a[rp].rs, mid + 1, r, ll, rr);
        else return query(a[lp].ls, a[rp].ls, l, mid, ll, mid) + query(a[lp].rs, a[rp].rs, mid + 1, r, mid + 1, rr);
    }
    int query(int l, int r, int L, int R){
        if (R > n) R = n;
        return query(root[l - 1], root[r], 1, n, L, R);
    }
    int work(int l, int r){
        int ret = 1;
        while (1){
            int tmp = query(l, r, 1, ret);
            if (tmp >= ret) ret = tmp + 1;
            else break;
        }
        return ret;
    }
}sgt;

int main(){
    read(N);
    int maxn = 0;
    for (int i = 1; i <= N; ++i)
        read(w[i]), chkmax(maxn, w[i]);
    sgt.init(maxn);
    read(Q);
    while (Q--){
        int l, r; read(l), read(r);
        printf("%d\n", sgt.work(l, r));
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/six_solitude/article/details/80969325