题目地址:http://codeforces.com/gym/101201/attachments
思路:
1.题目相当于对于一数,依次对[L,R]区间内的数取模,结果即为答案。
2.若对于区间内一大于v的数,其取模结果仍为本身,无作用。则问题变为快速寻找区间内第一个不大于v的数(区间内数无序)。
3.考虑二分区间[L,R],mid=(L+R)/2。若区间[L,mid]内最小值不大于v则说明第一个不大于v的数在该区间内,否则第一个不大于v的数在[mid+1,R]内,不断二分即可找到该数。
4.每次找到该数的位置后,下次区间查找范围为[pos,R],直到该区间内不存在小于v的数或v已经为0。
4.每次取模v%x<=v/2,则复杂度为log级别,查找同样为log级,时间可接受。
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define debug
using namespace std;
typedef long long LL;
const int maxn=200000+50;
int n,q;
LL a[maxn];
int preLog2[maxn];
LL stTable[maxn][20];
void st_prepare(int n,LL* arr)
{
preLog2[1]=0;
for(int i=2; i<=n; i++)
{
preLog2[i]=preLog2[i-1];
if((1<<preLog2[i]+1)==i)
{
++preLog2[i];
}
}
for(int i=n-1; i>=0; i--)
{
stTable[i][0]=arr[i];
for(int j=1; (i+(1<<j)-1)<n; j++)
{
stTable[i][j]=min(stTable[i][j-1],stTable[i+(1<<j-1)][j-1]);
}
}
}
LL query_min(int l,int r)
{
int len=r-l+1,k=preLog2[len];
return min(stTable[l][k],stTable[r-(1<<k)+1][k]);
}
int getPos(int l,int r,LL v)
{
int L=l,R=r,ans=n+1;
while(L<=R)
{
int mid=(L+R)/2;
if(query_min(L,mid)<=v)
{
ans=mid;
R=mid-1;
}
else
{
L=mid+1;
}
}
return ans;
}
int main()
{
#ifdef debu
freopen("in.txt","r",stdin);
#endif // debug
scanf("%d%d",&n,&q);
for(int i=0; i<n; i++) scanf("%lld",&a[i]);
st_prepare(n,a);
for(int i=0; i<q; i++)
{
LL v;
int l,r,pos;
scanf("%lld%d%d",&v,&l,&r);
l--,r--;
while(l<=r)
{
v%=a[l];
if(!v) break;
l=getPos(l,r,v);
}
printf("%lld\n",v);
}
return 0;
}