CGCDSSQ CodeForces - 475D(ST表&数论)
题目大意
给出一段序列与q个询问v,问存在多少的二元组 ,满足
解题思路
首先需要维护出一个ST表,其用于快速查询一段区间的gcd,然后对每个序列中的每个数,枚举所有以其为第一项的子序列的gcd,并用map保存其数量.但是直接暴力枚举,可能导致超时,这时需要用到一个性质,即从i开始向右,不断取共同的公因子,其至多会出现 种答案,因为,每次取公因子操作,假如公因子发生了变化,则至少会/2.为此,只需要每次找出i往后第一个gcd发生了的点设为j即可.那么这一段的贡献就是 而这只需要二分进行搜索即可.因此这个问题总的复杂度就是
AC代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int size=1e5+5;
const int lgsz=log2(size)+1;
int a[size];
struct ST{
int st[size][lgsz];
void build(int *a,int len){
for(int i=1;i<=len;i++) st[i][0]=a[i];
for(int j=1;(1<<j)<=len;j++){
for(int i=1;i+(1<<j)-1<=len;i++){
st[i][j]=__gcd(st[i][j-1],st[i+(1<<(j-1))][j-1]);
}
}
}
int query(int l,int r){
if(l==r) return st[l][0];
int k=log2(r-l+1);
return __gcd(st[l][k],st[r-(1<<k)+1][k]);
}
}sstt;
unordered_map<int,int> mp;
int find(int l,int r,int b,int val)
{
int ans=r;
while(l<=r)
{
int mid=(l+r)/2;
if(sstt.query(b,mid)==val)
{
l=mid+1;
}
else
{
r=mid-1;
ans=min(ans,r);
}
}
return ans;
}
int32_t main()
{
int n;
cin>>n;
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
sstt.build(a,n);
int gcd,loc;
int pre;
for(int i=1;i<=n;i++)
{
gcd=a[i];
loc=i;
while(true){
pre=loc;
loc=find(loc,n,i,gcd);
gcd=sstt.query(i,loc);
mp[gcd]+=loc-pre+1;
if(loc<n){
loc++;
gcd=sstt.query(i,loc);
}else break;
}
}
int q;
cin>>q;
int x;
while(q--){
cin>>x;
cout<<mp[x]<<endl;
}
}