题目大意:
给定[l,r]区间,区间内的数字任意组合。求最小不能被表示出来的数字。
思路:
- 如果没有元素1,则答案就是1
- 我们设当前能表示出来的最大整数是x,那么在小于等于x的元素中,任意选出一个元素b。x+b中的元素都可以表示出来。
- 所以我们只需要从1开始,枚举当前答案的前缀和。如果前缀和小于当前答案。那么该答案就是正确答案。
举例:
1 20 12 2 5 2,查询[1,6]能表出的最小整数。
ans = 1 , sum = 1
ans = 2 , sum = 5 , 观察1,2,2序列,可以表示出1,2,3,4,5的值。
ans = 5 , sum = 10 , 在1,2,2的基础上,加上5,则可以表示1-10的任意元素
ans = 10 , sum = 10
ans = 11 , sum = 10 则不能表示出11
代码实现:
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const ll maxn = 1e6+100;
ll tot,rt[maxn];
struct node{ll ls,rs,sum;}t[maxn<<5];
ll a[maxn],b[maxn],n,m,len;
ll getid(ll val){return lower_bound(b+1,b+len+1,val)-b;}
ll build(ll l,ll r){
ll now = ++tot;
t[now].sum = 0;
if(l==r) return now;
ll mid = l+r>>1;
t[now].ls = build(l,mid);
t[now].rs = build(mid+1,r);
return now;
}
ll update(ll l,ll r,ll rt,ll pos,ll val){
ll now = ++tot;
t[now] = t[rt] , t[now].sum+=val;//与一般权值线段树不同,维护前缀和
if(l==r) return now;
ll mid = l+r>>1;
if(pos<=mid) t[now].ls = update(l,mid,t[rt].ls,pos,val);
else t[now].rs = update(mid+1,r,t[rt].rs,pos,val);
return now;
}
ll query(ll u,ll v,ll l,ll r,ll pos){//<=pos sum
ll mid = l+r>>1;
ll k = t[t[u].ls].sum-t[t[v].ls].sum;
if(l==r) return t[u].sum-t[v].sum;
if(pos<=mid) return query(t[u].ls,t[v].ls,l,mid,pos);
else return query(t[u].rs,t[v].rs,mid+1,r,pos)+k;
}
int main(){
scanf("%lld%lld",&n,&m);
for(ll i=1;i<=n;i++)
scanf("%lld",&a[i]) , b[i] = a[i];
sort(b+1,b+n+1);
len = unique(b+1,b+n+1)-b-1; //离散化
rt[0] = build(1,len);
for(ll i=1;i<=n;i++)
rt[i] = update(1,len,rt[i-1],getid(a[i]),a[i]);
ll ans=0,l,r;
while(m--){
scanf("%lld%lld",&l,&r);
l = (l+ans)%n+1;
r = (r+ans)%n+1;
if(l>r) swap(l,r);
ll newans = 1; //当前答案
while(1){
ll pos = getid(newans),tmp;
if(b[pos]!=newans) pos--;
//在序列 1 2 3 5中查询4的id时
//geid()返回的是5,而我们要求的是小于等于newans的答案。
//所以要特判将id--
if(!pos) break; //wa了两发
tmp = query(rt[r],rt[l-1],1,len,pos);
//查询下一次能表示的最大值
if(tmp>=newans) newans = tmp+1; //查询下一个
else break; //当前前缀和不足以表示当前答案,则答案为newans
}
printf("%lld\n",newans);
ans = newans;
}
return 0;
}