题目连接:http://acm.hdu.edu.cn/showproblem.php?pid=5726
【题意】给定n个数a1,a2,......,an,对于给定的l,r,求出[l,r]区间的gcd(l,r)值,同时输出1-n范围内任意l',r'(l'<r'),使得[l',r‘]区间gcd(l',r')=gcd(l,r)的不同的(l',r')的个数。
【分析】先使用ST表预处理以便求区间gcd的值。一端不变,区间gcd的值随区间的增大只有可能相等或者减小,而且每次减小至少缩小2倍,及一端不变后,不同的区间gcd值最多有log(n)个。如此预处理枚举左端点,二分求出相同区间gcd值的长度,用map累计该区间gcd值的数量。最后问题求解的时候直接输出答案。
【代码】
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#include<vector>
#include<map>
using namespace std;
#define MAXN 100100
#define LL long long
int gcd[MAXN][30];
int lg2[MAXN];
int n;
map<int,LL> ans;
int getgcd(int a,int b){
if(a%b==0)
return b;
return getgcd(b,a%b);
}
void init(){
for(int i=2;i<=n;++i)
lg2[i]=lg2[i/2]+1;
for(int j=1;(1<<j)<=n;++j)
for(int i=1;i+(1<<j)-1<=n;++i)
gcd[i][j]=getgcd(gcd[i][j-1],gcd[i+(1<<(j-1))][j-1]);
}
int forgcd(int l,int r){
int k=lg2[r-l+1];
return getgcd(gcd[l][k],gcd[r-(1<<k)+1][k]);
}
int main()
{
int T,cas=1,q,l,r;
cin>>T;
while(T--){
cin>>n;
ans.clear();
for(int i=1;i<=n;++i)
scanf(" %d",&gcd[i][0]);
init();
for(int i=1;i<=n;++i){
ans[forgcd(i,i)]++;
for(int j=i+1;j<=n;++j){
if(forgcd(i,j)==forgcd(i,j-1))
{
int l=j,r=n,mid,tmp=forgcd(i,j-1);
int fin=n+1;
while(l<=r)
{
mid=(l+r)>>1;
if(forgcd(i,mid) < tmp)
{
r = mid - 1;
}
else
{
l = mid + 1;
fin = mid;
}
}
ans[tmp]+=fin-j+1;
j=fin;
continue;
}
ans[forgcd(i,j)]++;
}
}
cin>>q;
printf("Case #%d:\n",cas++);
for(int i=0;i<q;++i){
scanf("%d %d",&l,&r);
int tmp=forgcd(l,r);
printf("%d %I64d\n",tmp,ans[tmp]);
}
}
}