题意
给你一个区间,然后多次询问,每次给出一个 x ,询问有多少个区间 ( l , r ) (l,r) (l,r)使得 g c d ( l , l + 1 , l + 2 , . . . , r ) = x gcd(l,l+1,l+2,...,r) =x gcd(l,l+1,l+2,...,r)=x
解析
因为查询次数过大,并且 g c d gcd gcd 属于可重复贡献问题,可以用st表维护。
我们可以发现,对于某个区间,当其右侧添加新的数的时候,区间的最大公约数会不变或者减少。
比如,对于原区间的最大公约数
x
x
x,当添加右端点时
x
,
x
,
x
,
x
,
x
,
a
,
a
,
a
,
b
,
b
,
c
,
c
x,x,x,x,x,a,a,a,b,b,c,c
x,x,x,x,x,a,a,a,b,b,c,c 则
(
a
≤
b
≤
c
)
(a\leq b\leq c)
(a≤b≤c)
所以我们可以遍历区间,对于每个点固定为左端点,然后二分查找其右端点,然后累计区间长度作为答案
小优化:因为只有查询没有修改,所以可以离线保存所有查询值,只保存需要查询的 g c d gcd gcd 值
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define inf 0x3f3f3f3f
const int N=1e5+5;
int n,q;
int a[N],f[N][21],minn[N][21];
map<int,int>mp;
int gcd(int a,int b){
return b?gcd(b,a%b):a;
}
void init(){ //ST表O(nlogn)初始化
for(int i=1;i<=n;i++) f[i][0]=a[i];
for(int j=1;(1<<j)<=n;j++){
for(int i=1;i+(1<<j)-1<=n;i++){
f[i][j]=gcd(f[i][j-1],f[i+(1<<(j-1))][j-1]);
}
}
}
inline int query(int l,int r){ //ST表O(1)查询
int k=log2(r-l+1);
return gcd(f[l][k],f[r-(1<<k)+1][k]);
}
void solve(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
init();
cin>>q;
vector<int>p;
while(q--){ //离线存储查询
int x;
cin>>x;
p.push_back(x);
mp[x]=0;
}
for(int i=1;i<=n;i++){ //二分预处理 gcd(x,y)=d 的区间个数
int pos=i;
while(pos<=n){
int l=pos,r=n,d=query(i,pos);
while(l<r){
int mid=l+r+1>>1;
if(query(i,mid)>=d) l=mid;
else r=mid-1;
}
if(mp.count(d)) mp[d]+=l-pos+1; //存在查询才保存
pos=l+1;
}
}
for(auto it:p){
cout<<mp[it]<<endl;
}
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int t=1;
// cin>>t;
while(t--) solve();
return 0;
}