loj2174 「FJOI2016」神秘数

时间:2022-10-15 15:18:59

先考虑一下一个集合怎么用 \(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;
}