loj2174 「FJOI2016」神秘数

先考虑一下一个集合怎么用 \(O(n)\) 时间求出来,然后用主席树推广到一个序列就可以了。大致思想就是考虑一个数的权值和它前面的数的和的关系。

#include <algorithm>
#include <iostream>
#include <cstdio>
using namespace std;
int n, a[100005], cnt, b[100005], m, uu, vv, rot[100005], tot;
struct Node{
    int l, r, sum;
}nd[5000005];
int build(int l, int r){
    int re=++tot;
    if(l==r)    ;
    else{
        int mid=(l+r)>>1;
        if(l<=mid)  nd[re].l = build(l, mid);
        if(mid<r)   nd[re].r = build(mid+1, r);
    }
    return re;
}
int update(int pre, int l, int r, int x){
    int re=++tot;
    nd[re] = nd[pre];
    nd[re].sum += b[x];
    if(l==r)    ;
    else{
        int mid=(l+r)>>1;
        if(x<=mid)  nd[re].l = update(nd[pre].l, l, mid, x);
        if(mid<x)   nd[re].r = update(nd[pre].r, mid+1, r, x);
    }
    return re;
}
int query(int pre, int now, int l, int r, int x, int y){
    if(b[l]>=x && b[r]<=y)
        return nd[now].sum-nd[pre].sum;
    else if(l==r)
        return 0;
    else{
        int mid=(l+r)>>1;
        int re=0;
        if(x<=b[mid])   re += query(nd[pre].l, nd[now].l, l, mid, x, y);
        if(b[mid]<y)    re += query(nd[pre].r, nd[now].r, mid+1, r, x, y);
        return re;
    }
}
int getAns(int uu, int vv){
    int sum=0, lst=0;
    while(true){
        int tmp=query(rot[uu-1], rot[vv], 1, cnt, lst+1, sum+1);
        if(!tmp)
            return sum+1;
        lst = sum + 1;
        sum += tmp;
    }
}
int main(){
    cin>>n;
    for(int i=1; i<=n; i++){
        scanf("%d", &a[i]);
        b[i] = a[i];
    }
    sort(b+1, b+1+n);
    cnt = unique(b+1, b+1+n) - b - 1;
    for(int i=1; i<=n; i++)
        a[i] = lower_bound(b+1, b+1+cnt, a[i]) - b;
    rot[0] = build(1, cnt);
    for(int i=1; i<=n; i++)
        rot[i] = update(rot[i-1], 1, cnt, a[i]);
    cin>>m;
    while(m--){
        scanf("%d %d", &uu, &vv);
        printf("%d\n", getAns(uu, vv));
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/poorpool/p/9086913.html