题意:
给出一个长度为 n n n 的数组, m m m 次询问,询问给出一个区间 ( l , r ) (l,r) (l,r),求这个区间的数任意相加,不能得到的最小的和是多少,询问采用强制在线。
题解:
先考虑给定一个集合,怎么求不能得到的最小的和。
1. 1. 1. 如果集合中不存在 1 1 1 ,那么答案就是 1 1 1
2. 2. 2. 否则,设 集合中的数 b 1 ≤ b 2 ≤ b 3 ≤ . . . ≤ b k b_1 \leq b_2 \leq b_3 \leq ... \leq b_k b1≤b2≤b3≤...≤bk , s i = ∑ j = 1 i b j s_i= \sum\limits_{j=1}^{i}b_j si=j=1∑ibj
假设前 i i i 个数能构成 ( 1 , x ) (1,x) (1,x) ,如果 b i + 1 ≤ x + 1 b_{i+1} \leq x+1 bi+1≤x+1 ,那么就一定能构成 ( 1 , b i + 1 + x ) (1,b_{i+1}+x) (1,bi+1+x) 。为什么?如果 b i + 1 b_{i+1} bi+1 大于 x + 1 x+1 x+1 ,那么 b i + 1 b_{i+1} bi+1 不管怎么加,得到的数永远都会 ≥ b i + 1 \geq b_{i+1} ≥bi+1 ,那么 x + 1 x+1 x+1 就不能构成,即不能连续下去。
由此说明,其实 x x x 就等于 s i s_i si 。也就是说,我们只需每次找到 ( 1 , x + 1 ) (1,x+1) (1,x+1) 内的所有数,然后求和,假如 s u m = = x sum==x sum==x ,那么更新不了 ,否则就可以更新为 ( 1 , s u m ) (1,sum) (1,sum) 。那么怎么找某段区间的值在某段区间的和呢?这就是经典的主席树维护一下即可。
还有一点,在求答案时,暴力更新
(
1
,
x
)
(1,x)
(1,x)即可,因为 每次更新都是类似于斐波那契这样加起来,而数值范围在
1
e
9
1e9
1e9内,差不多就是
l
o
g
log
log 级别的复杂度。总的时间复杂度为
O
(
n
l
o
g
l
o
g
)
O(nloglog)
O(nloglog)
代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<stack>
#include<set>
#include<ctime>
#define iss ios::sync_with_stdio(false)
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
const int mod=1e9+7;
const int MAXN=1e6+5;
const int inf=0x3f3f3f3f;
int a[MAXN];
int tree[MAXN];
int cnt=0;
std::vector<int> v;
int len;
struct Node
{
int l,r;
ll sum;
}node[MAXN*40];
int cal(int x)
{
return lower_bound(v.begin(),v.end(),x)-v.begin()+1;
}
int cal_rk(ll x)
{
if(v[len-1]<=x) return len;
int pos=upper_bound(v.begin(),v.end(),x)-v.begin()+1;
pos--;
return pos;
}
void insert(int l,int r,int pre,int &now,int x,int val)
{
node[++cnt]=node[pre];
now=cnt;
node[now].sum+=val;
if(l==r) return;
int mid=(l+r)>>1;
if(x<=mid) insert(l,mid,node[pre].l,node[now].l,x,val);
else insert(mid+1,r,node[pre].r,node[now].r,x,val);
}
ll query(int l,int r,int pre,int now,int find_right)
{
if(r<=find_right) return node[now].sum-node[pre].sum;
ll ans=0;
int mid=(l+r)>>1;
if(l<=find_right) ans+=query(l,mid,node[pre].l,node[now].l,find_right);
if(mid+1<=find_right) ans+=query(mid+1,r,node[pre].r,node[now].r,find_right);
return ans;
}
int main()
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
v.push_back(a[i]);
}
sort(v.begin(),v.end());
v.erase(unique(v.begin(),v.end()),v.end());
len=v.size();
for(int i=1;i<=n;i++)
{
insert(1,n,tree[i-1],tree[i],cal(a[i]),a[i]);
}
ll last=0;
while(m--)
{
int l,r;
scanf("%d%d",&l,&r);
l=(l+last)%n+1;
r=(r+last)%n+1;
if(l>r) swap(l,r);
ll x=0;
while(1)
{
int p=cal_rk(x+1);
if(p==0) break;
ll sum=query(1,n,tree[l-1],tree[r],p);
if(sum==x) break;
else x=sum;
}
printf("%lld\n",x+1);
last=x+1;
}
}