题意:n个数,m次询问,每次给你一个询问v,l,r,问你v%a[l]%a[l+1]%...%a[r]是多少。
a%b,结果要么不变,要么至少缩小到a的一半,于是用线段树,每次询问当前区间最靠左侧的小于等于当前数的值是多少,只需不超过log次询问就能使该数模完,就行了。
O(n(logn)^2)。
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
int n,m;
ll minv[800005],a[200005];
void buildtree(int rt,int l,int r){
if(l==r){
scanf("%I64d",&minv[rt]);
a[l]=minv[rt];
return;
}
int m=(l+r>>1);
buildtree(rt<<1,l,m);
buildtree(rt<<1|1,m+1,r);
minv[rt]=min(minv[rt<<1],minv[rt<<1|1]);
}
int pos;
int find(ll v,int rt,int l,int r){
if(l==r){
return l;
}
int m=(l+r>>1);
if(minv[rt<<1]<=v){
return find(v,rt<<1,l,m);
}
else{
return find(v,rt<<1|1,m+1,r);
}
}
bool query(int ql,int qr,ll v,int rt,int l,int r){
if(ql<=l && r<=qr){
if(minv[rt]<=v){
pos=find(v,rt,l,r);
return 1;
}
return 0;
}
int m=(l+r>>1);
if(ql<=m){
if(query(ql,qr,v,rt<<1,l,m)){
return 1;
}
}
if(m<qr){
if(query(ql,qr,v,rt<<1|1,m+1,r)){
return 1;
}
}
return 0;
}
int main(){
// freopen("j.in","r",stdin);
ll z;
int x,y;
scanf("%d%d",&n,&m);
buildtree(1,1,n);
for(int i=1;i<=m;++i){
scanf("%I64d%d%d",&z,&x,&y);
while(x<=y && query(x,y,z,1,1,n)){
z%=a[pos];
x=pos+1;
}
printf("%I64d\n",z);
}
return 0;
}