可以用st表预处理出区间gcd,然后O(1)查询。
我们考虑枚举左端点,随着右端点的后移,区间gcd最多变化log(n)次,因为每次变化gcd都至少要减小一半,所以显然。
那么我们可以枚举左端点,然后二分gcd每次变化的位置,用map记录答案。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<map>
#define N 300003
#define LL long long
using namespace std;
int n,m,f[N][20],l[N];
map<int,LL> ans;
int a[N];
int gcd(int x,int y)
{
int r;
while (y){
r=x%y;
x=y;
y=r;
}
return x;
}
void solve()
{
for (int i=1;i<=n;i++) f[i][0]=a[i];
for (int j=1;j<=17;j++)
for (int i=1;i<=n;i++)
if (i+(1<<j)-1<=n) f[i][j]=gcd(f[i][j-1],f[i+(1<<(j-1))][j-1]);
int j=0;
for (int i=1;i<=n;i++){
if ((1<<(j+1))<=i) j++;
l[i]=j;
}
}
int calc(int x,int y)
{
int k=l[y-x];
return gcd(f[x][k],f[y-(1<<k)+1][k]);
}
int work(int pos,int l,int r,int x)
{
int ans=r+1;
while (l<=r) {
int mid=(l+r)/2;
if (calc(pos,mid)!=x) ans=min(ans,mid),r=mid-1;
else l=mid+1;
}
if (ans==r) return ans;
return ans-1;
}
int main()
{
freopen("a.in","r",stdin);
scanf("%d",&n);
for (int i=1;i<=n;i++) scanf("%d",&a[i]);
solve();
for (int i=1;i<=n;i++) {
int now=a[i]; int x=i;
while (true) {
int last=x;
x=work(i,last,n,now);
now=calc(i,x);
ans[now]+=x-last+1;
if (x>=n) break;
x++; now=calc(i,x);
}
}
scanf("%d",&m);
for (int i=1;i<=m;i++) {
int x; scanf("%d",&x);
cout<<ans[x]<<endl;
}
}