Description
给出一个长度为n的序列a[1],…,a[n],q次查询,对于每次查询(l,r),输出gcd(a[l],…,a[r]),并且输出满足条件的区间[ll,rr]的个数,使得gcd(a[ll],…,a[rr])=gcd(a[l],…,a[r])
Input
第一行为一整数T表示用例组数,每组用例首先属于序列长度n,之后n个整数a[i]表示该序列,然后输出查询次数q,之后q行每行两个整数li,ri表示一次查询(n,q<=10^5,0< ai<=10^9)
Output
对于每次查询,输出gcd(a[l],…,a[r]),并且输出满足条件的区间[ll,rr]的个数(ll<=rr)
Sample Input
1
5
1 2 4 6 7
4
1 5
2 4
3 4
4 4
Sample Output
Case #1:
1 8
2 4
2 4
6 1
Solution
首先用ST表求出区间gcd,那么第一个问题就可以O(1)查询,对于第二个问题,用gcd(l,r)表示gcd(a[l],…,a[r]),固定左端点l,随着右端点r的递增,gcd(l,r)阶梯型递减,而且gcd(l,r)减的很快,最多有log(10^9)不同的gcd(l,r)值,故对每个gcd,枚举左端点,二分右端点即可得到答案
Code
#include<cstdio>
#include<iostream>
#include<cmath>
#include<map>
using namespace std;
typedef long long ll;
#define maxn 111111
int T,n,q,a[maxn],res=1;
map<int,ll>m;
int gcd(int a,int b)
{
return b?gcd(b,a%b):a;
}
int st[maxn][22];
void ST()
{
for(int i=0;i<n;i++)st[i][0]=a[i];
int k=(int)(log(1.0*n)/log(2.0));
for(int j=1;j<=k;j++)
for(int i=0;i+(1<<j)<=n;i++)
st[i][j]=gcd(st[i][j-1],st[i+(1<<(j-1))][j-1]);
}
int query(int l,int r)
{
int k=(int)(log(1.0*(r-l+1))/log(2.0));
return gcd(st[r-(1<<k)+1][k],st[l][k]);
}
int main()
{
scanf("%d",&T);
while(T--)
{
m.clear();
scanf("%d",&n);
for(int i=0;i<n;i++)scanf("%d",&a[i]);
ST();
for(int i=0;i<n;i++)
{
int t,ll=i;
while(ll<n)
{
t=query(i,ll);
int l=ll,r=n-1;
while(l<r)
{
int mid=(l+r+1)>>1;
if(query(i,mid)>=t)l=mid;
else r=mid-1;
}
m[t]+=l-ll+1;
ll=l+1;
}
}
scanf("%d",&q);
printf("Case #%d:\n",res++);
while(q--)
{
int l,r;
scanf("%d%d",&l,&r);
l--,r--;
printf("%d %I64d\n",query(l,r),m[query(l,r)]);
}
}
return 0;
}