题目描述
modmod是取余运算,在程序中用符号”%%”来表示。
如3%7=3,7%5=2,0%4=0。3%7=3,7%5=2,0%4=0。
Ocean用巧妙的方法得到了一个序列,该序列有NN个元素,我们用数组aa来记录(下标从00到N−1N−1)。
Ocean定义f[i]=(((i%a[0])%a[1])%…)%a[N−1]。f[i]=(((i%a[0])%a[1])%…)%a[N−1]。
现在Ocean会给出QQ次查询,每次给定一个区间[L,R],[L,R],他想快速知道∑Ri=Lf[i]∑i=LRf[i] (即f[L]+…+f[R]f[L]+…+f[R])的值。
输入
第一行输入一个整数TT,代表有TT组测试数据。
每组数据占多行,第一行输入一个整数NN,代表元素个数。
下面一行输入NN个整数aiai。
下面一行输入一个整数QQ,代表QQ次查询。
接下来QQ行,每行输入两个整数L,RL,R,代表查询的区间。
注:1<=T<=20,1<=N,Q<=1000,1<=ai<=100000,1<=L<=R<=100000。1<=T<=20,1<=N,Q<=1000,1<=ai<=100000,1<=L<=R<=100000。
输出
对每组测试数据,依次输出QQ行,每行输出对应的查询结果。
样例输入
2
5
5 4 3 2 1
4
1 100000
2 100000
3 100000
4 100000
5
5 5 5 5 5
4
1 100000
2 100000
3 100000
4 100000
样例输出
0
0
0
0
200000
199999
199997
199994
来源
CZY
觉得这题蛮有意思,就写写。 其实只要仔细想想就不是那么难,其实就是暴力,不过有的地方我们可以去进行一些优化,然后就可以过了。
代码
ac代码
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
typedef long long LL ;
const int MAXN = 100000+10;
const int MAXM = 1e5 ;
const int mod = 1e9+7 ;
int arr[1000+10];
int sum[MAXN];
int main(){
int t;scanf("%d",&t);
while(t--){
int n;scanf("%d",&n);
int flag=0;int sz=1; // 这里我们可以去重,只有后面的数字比前面的小才起作用。这样做同时也将序列变成了单调的,才可以用二分
scanf("%d",&arr[0]); arr[0]=-arr[0];
for(int i=1;i<n;i++){
int a;scanf("%d",&a);
if(a==1) flag=1;
if(a<abs(arr[sz-1])) arr[sz++]=-a;// 为什么这里 都要取一下负号呢。看下面二分的部分你就会明白了,我这里是为了二分方便
}
// for(int i=0;i<sz;i++) printf("%d ",arr[i]);
// printf("sz==%d\n",sz);
if(!flag){//只要数组中有 1,那么所有的f[i]都会是零。
sum[1]=1;
for(int i=2;i<=MAXN;i++){
int val=i;
for(int j;;){//查找第一个可以对它取模有作用的位置
j=lower_bound(arr,arr+sz,-val)-arr;
if(j==sz||val==0) break;// 如果没有比它大的,即取模对其没有作用,或者已经取模为0了。这时候可以直接返回。
val=val%(-arr[j]);
}
sum[i]=sum[i-1]+val;
}
}
int q;int l,r;
scanf("%d",&q);
while(q--){
scanf("%d%d",&l,&r);
if(flag) puts("0");
else printf("%d\n",sum[r]-sum[l-1]);
}
}
return 0;
}