考虑一个有序序列,有可重集A包含 [ l , r ] [l,r] [l,r]内所有数(记作 A = [ l , r ] A=[l,r] A=[l,r])
若我们有,B= [ l , r − 1 ] [l,r-1] [l,r−1]可表示的数为 [ 1 , s u m B ] [1,sumB] [1,sumB](sumB为B中所有元素和),我们说B是理想的可重集(表示区间是连续的,A的最小不能表示数可能继续增大)。
参考背包原理,可以发现新的可表示区间为: [ 1 , s u m B ] ∪ [ 1 + a [ r ] , s u m B + a [ r ] ] [1,sumB]∪[1+a[r], sumB+a[r] ] [1,sumB]∪[1+a[r],sumB+a[r]]
尝试用B推出A。
①新的区间是连续的:答案是sumB+a[r]。A是新的理想可重集。
②一旦区间不连续:A和 [ l , r 到 n ] [l, r到n] [l,r到n]的最小不能表示数都是sumB+1,(因为序列有序,最小的可选数就是现在的,更大的数)
对于一个区间,排序后计算第一个sum[i] + 1 < a[i + 1]
。
考虑一个用主席树处理过值域的区间。(主席树)
现在我们知道该区间某个值域的所有数的和,考虑优化上述算法:
设:当前处理的理想区间的和为SUM,最大数为X,那么待选数是大于X的,所有小于SUM+1的数是不会使区间产生裂缝的。 因此可以直接把 [ X , S U M + 1 ] [X,SUM+1] [X,SUM+1]的数全部一次性加入理想区间里。
容易发现理想可重集的可表示区间的长度是倍增的。
s i z e o f [ L , R ] − > s i z e o f [ L , R ] + s i z e o f [ L + a [ R + 1 ] , R + a [ R + 1 ] ] ; sizeof [L, R] -> sizeof[L,R]+sizeof[L+a[R+1], R+a[R+1]]; sizeof[L,R]−>sizeof[L,R]+sizeof[L+a[R+1],R+a[R+1]];
附上代码:
#include
#include
#include
#include
using namespace std;
typedef long long xt;
#define R(i) for(int i=1; i<=n; ++i)
#define rep(i, x, y) for(int i=x; i<=y; ++i)
const int N=1e5+10, SI=1e7;
const int D=1e9+10;
int n,m;
int a[N];
int amt, rt[SI];
struct nod {
int v;
int ls, rs;
int ihs(const nod &x) {
ls = x.ls;
rs = x.rs;
}
}tr[SI];
#define lc tr[p].ls
#define rc tr[p].rs
#define calmid \
int mid=(L+R)>>1;
int cha(int f, int L, int R, int x, int d) {
int p=++amt;
tr[p].v = tr[f].v+d;
tr[p].ihs(tr[f]);
if(L==R) return p;
else {
calmid;
if(x<=mid) lc=cha(lc, L, mid, x, d);
else rc=cha(rc, mid+1, R, x, d);
}
return p;
}
xt iqr(int p, int L, int R, int l, int r) {
int c=0;
if(!p) return 0;
if(l<=L&&R<=r) return tr[p].v;
else {
calmid;
if(l<=mid) c+=1ll*iqr(lc, L, mid, l, r);
if(mid+1<=r) c+=1ll*iqr(rc, mid+1, R, l, r);
}
return c;
}
int calc(int l, int r) {
int x=0, s=0, tmp=0;
while(tmp=(iqr(rt[r],1,D,x+1,s+1) - iqr(rt[l-1],1,D,x+1,s+1))) {
x=s+1; s+=tmp;
}
return s+1;
}
int in() {
int nt, x=0;
while(!isdigit(nt=getchar())); x=(nt^'0');
while(isdigit(nt=getchar())) x=(x<<1)+(x<<3)+(nt^'0');
return x;
}
int main() {
freopen("niuniunum.in", "r", stdin);
freopen("niuniunum.out", "w", stdout);
int x, y;
scanf("%d", &n);
for(int i=1; i<=n; ++i) x=in(), rt[i]=cha(rt[i-1],1,D,x,x);
scanf("%d", &m);
for(int i=1; i<=m; ++i) {
x=in(), y=in();
printf("%d\n", calc(x, y));
}
return 0;
}