Problem Description
The shorter, the simpler. With this problem, you should be convinced of this truth.
You are given an array A of N postive integers, and M queries in the form (l,r). A function F(l,r) (1≤l≤r≤N) is defined as:
F(l,r)={AlF(l,r−1) modArl=r;l<r.
You job is to calculate F(l,r), for each query (l,r).
You are given an array A of N postive integers, and M queries in the form (l,r). A function F(l,r) (1≤l≤r≤N) is defined as:
F(l,r)={AlF(l,r−1) modArl=r;l<r.
You job is to calculate F(l,r), for each query (l,r).
Input
There are multiple test cases.
The first line of input contains a integer T, indicating number of test cases, and T test cases follow.
For each test case, the first line contains an integer N(1≤N≤100000).
The second line contains N space-separated positive integers: A1,…,AN (0≤Ai≤109).
The third line contains an integer M denoting the number of queries.
The following M lines each contain two integers l,r (1≤l≤r≤N), representing a query.
The first line of input contains a integer T, indicating number of test cases, and T test cases follow.
For each test case, the first line contains an integer N(1≤N≤100000).
The second line contains N space-separated positive integers: A1,…,AN (0≤Ai≤109).
The third line contains an integer M denoting the number of queries.
The following M lines each contain two integers l,r (1≤l≤r≤N), representing a query.
Output
For each query
(l,r), output F(l,r) on one line.
Sample Input
1 3 2 3 3 1 1 3
Sample Output
2
题意:
给你一个n,n个数
m个询问,每次询问你 l,r,, a[l] % a[l+1] % a[l+2] %……a[r] 结果是多少
思路:每次取模比自己小的数, 最多logn次就到0了, 因为 你每次取模自己小的数, 取模之后 最大的数就是 n/2, 每次都是n/2, logn次就是0了, 关键是怎么找每个数取模之后后面的第1个比他小的数,两种做法, 一个是 rmq+二分, 另一个是线段树,线段树可以解决好多问题的。。。
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
using namespace std;
const int INF = 1e9 + 7;
const int maxn = 1e5 + 10;
int minx[maxn][50], a[maxn], n, lg2[maxn];
void RMQ()
{
for(int i = 2;i <= n; i++)lg2[i] = lg2[i/2]+1; //还快一点
for(int i = 1;i <= n; i++) minx[i][0] = a[i];
for(int j = 1;(1<<j) <= n; j++)
{
for(int i = 1; i+(1<<j)-1 <= n; i++){
minx[i][j] = min(minx[i][j-1],minx[i+(1<<(j-1))][j-1]);
}
}
}
int check(int l, int r)
{
int k = lg2[r-l+1];
return min(minx[l][k], minx[r-(1<<k)+1][k]);
}
int main()
{
int T, q, l, r;
// RMQ();
scanf("%d", &T);
while(T--)
{
scanf("%d", &n);
for(int i = 1; i <= n; i++)
scanf("%d", &a[i]);
RMQ();
scanf("%d", &q);
while(q--)
{
scanf("%d%d", &l, &r);
if(l == r)
{
printf("%d\n", a[l]);
continue;
}
int k = a[l];
l++;
int la, ra, mid, tmp, flag;
while(l <= r && k)
{
la = l, ra = r, flag = 0;
while(la <= ra) //这样快点
{
mid = (la+ra) >> 1;
if(check(la, mid) <= k)
ra = mid - 1, tmp = mid, flag = 1;
else if(check(mid, ra) <= k)
la = mid + 1, tmp = mid, flag = 1;
else
break;
}
if(!flag) break;
l = tmp+1;
k %= a[tmp];
}
printf("%d\n", k);
}
}
return 0;
}
线段树代码:
我们可以使用线段树求出区间内小于某个值的最前一个位置,具体方法就是:父节点记录区间最小值,接着当这一整段的最小值小于等于给定的值时就递归进此子树(另一棵子树还是可能递归,因为可能前一个子树包含的区间大于所求的区间),这样我们知道第一次递归到叶子节点时就一定是最前一个小于等于此值的位置(如果有这个值的话)。
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<cmath>
#include<vector>
#include<string>
#include<cstdio>
#include<cstring>
#include<stdlib.h>
#include<iostream>
#include<algorithm>
using namespace std;
#define eps 1E-8
/*注意可能会有输出-0.000*/
#define Sgn(x) (x<-eps? -1 :x<eps? 0:1)//x为两个浮点数差的比较,注意返回整型
#define Cvs(x) (x > 0.0 ? x+eps : x-eps)//浮点数转化
#define zero(x) (((x)>0?(x):-(x))<eps)//判断是否等于0
#define mul(a,b) (a<<b)
#define dir(a,b) (a>>b)
typedef long long ll;
typedef unsigned long long ull;
const int Inf=1<<30;
const double Pi=acos(-1.0);
const int Mod=1e9+7;
const int Max=200010<<2;
int nnum[Max],cnt,flag;
int segtr[Max];
struct node
{
int mmin,mpos;
}res;
void Upnow(int now,int next)
{
segtr[now]=min(segtr[next],segtr[next|1]);
return;
}
void Create(int sta,int enn,int now)
{
if(sta==enn)
{
scanf("%d",&segtr[now]);
nnum[cnt++]=segtr[now];//记录每个位置的值
return;
}
int mid=dir(sta+enn,1);
int next=mul(now,1);
Create(sta,mid,next);
Create(mid+1,enn,next|1);
Upnow(now,next);
return;
}
void Query(int sta,int enn,int now,int x,int y,int z)
{
if(sta>=x&&enn<=y)
{
if(sta==enn)//到叶子节点
{
flag=1;//表示只能到一次叶子节点
if(segtr[now]<=z)//找到
{
res.mmin=segtr[now];
res.mpos=sta;
}
return;
}
if(segtr[now]>z)//这一段不需要再递归
return;
}
int mid=dir(sta+enn,1);
int next=mul(now,1);
if(mid>=x&&!flag&&segtr[next]<=z)//之前没到叶子,子树区间最小值小于等于给定的值
Query(sta,mid,next,x,y,z);
if(mid<y&&!flag&&segtr[next|1]<=z)
Query(mid+1,enn,next|1,x,y,z);
return;
}
int main()
{
int t,n,m;
int lef,rig;
scanf("%d",&t);
while(t--)
{
cnt=1;
scanf("%d",&n);
Create(1,n,1);
scanf("%d",&m);
for(int i=1;i<=m;++i)
{
scanf("%d %d",&lef,&rig);
if(lef==rig)//只有一个值
{
printf("%d\n",nnum[lef]);
continue;
}
int ans=nnum[lef];
lef++;
while(1)
{
res.mmin=Inf,res.mpos=-1;
flag=0;
Query(1,n,1,lef,rig,ans);
if(ans>=res.mmin)//成功取模
{
ans=ans%res.mmin;
lef=res.mpos+1;
}
else
break;
if(lef>rig||ans==0)//结束条件
break;
}
printf("%d\n",ans);
}
}
return 0;
}