我们可以得到一个充分必要条件:
我们记vi为区间内小于等于i的数的和
那么答案为第一个i,满足vi<i
如果我们现在求得at,val
其中val是区间内小于等于at的数的和
那么对于小于等于val的i,都可以被某个子集和表示出来
于是,我们可以转移到
(val,小于等于val的数的和)
这时如果val==小于等于val的数的和
我们可以转移到(val+1,小于等于val+1的数的和)
如果小等于val+1的数的和<val+1,那我们就输出val+1,break
这其中查询一个区间内小于等于某数的和可以用主席树来实现
可以看出,at增长很快,是log级别的…
然后我不会证复杂度…
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
int n,m;
#define Maxn 100010
#define V 6000010
int a[Maxn];
struct Node{
int ls,rs;
int sumv;
}tree[V];
int root[Maxn];
int cnt=0;
void insert(int &k,int x,int pos,int l,int r){
k=++cnt;
tree[k]=tree[x];
tree[k].sumv+=pos;
if(l==r)return;
int mid=(l+r)>>1;
if(pos<=mid)insert(tree[k].ls,tree[x].ls,pos,l,mid);
else insert(tree[k].rs,tree[x].rs,pos,mid+1,r);
}
int Query(int k1,int k2,int l,int r,int L,int R){
if(l==L&&r==R)return tree[k2].sumv-tree[k1].sumv;
int mid=(l+r)>>1;
if(R<=mid)return Query(tree[k1].ls,tree[k2].ls,l,mid,L,R);
else if(mid<L)return Query(tree[k1].rs,tree[k2].rs,mid+1,r,L,R);
else return Query(tree[k1].ls,tree[k2].ls,l,mid,L,mid)+Query(tree[k1].rs,tree[k2].rs,mid+1,r,mid+1,R);
}
inline void rd(int &x){
x=0;char ch=getchar();
while(ch<'0'||ch>'9')ch=getchar();
while(ch>='0'&&ch<='9'){
x=x*10+ch-'0';
ch=getchar();
}
}
int main(){
rd(n);
for(register int i=1;i<=n;++i){
rd(a[i]);
insert(root[i],root[i-1],a[i],1,1e9);
}
rd(m);
int l,r;
for(register int tt=1;tt<=m;++tt){
rd(l);rd(r);
int at=1,val=Query(root[l-1],root[r],1,1e9,1,1);
while(at<=val){
at=val;
val=Query(root[l-1],root[r],1,1e9,1,at);
if(at==val){
at++;
if(at<=1000000000)val=Query(root[l-1],root[r],1,1e9,1,at);
}
}
printf("%d\n",at);
}
return 0;
}