Description
给出长度为(n(nleq10^5))的序列({a_n}),给出(q(qleq3 imes10^5))个(x),对于每个(x),求满足(gcd{a_L...a_R})的数对((L,R))有多少个。
Solution
(f[i][x])表示以(i)为左端点的区间中,(gcd=x)的有多少个。
由右到左进行转移,赋初值(f[i][a_i]=1):$$f[i][gcd(x,a_i)]=sum f[i+1][x]$$因为对于以(a_i)为左端点的区间(gcd),最多只有(loga_i)种取值(每次(gcd)至少减小一半),所以第二维其实只有(loga_i)个有值。我们可以用map
代替第二维,并滚动第一维。
时间复杂度(O(nlog^2n))。
Code
//CGCDSSQ
#include <cstdio>
#include <map>
using namespace std;
int const N=1e5+10;
int n,m,a[N];
map<int,long long> ans,f[2];
map<int,long long>::iterator it;
int gcd(int x,int y) {return x%y?gcd(y,x%y):y;}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
int c=0;
f[0][a[n]]=1; ans[a[n]]++;
for(int i=n-1;i>=1;i--)
{
c^=1; f[c].clear(); f[c][a[i]]=1;
for(it=f[c^1].begin();it!=f[c^1].end();it++)
f[c][gcd(it->first,a[i])]+=it->second;
for(it=f[c].begin();it!=f[c].end();it++) ans[it->first]+=f[c][it->first];
}
scanf("%d",&m);
for(int i=1;i<=m;i++)
{
int x; scanf("%d",&x);
printf("%lld
",ans[x]);
}
return 0;
}
P.S.
“以(a_i)为左端点的区间(gcd),最多只有(loga_i)种取值”感觉这个结论还蛮有用的。
第一次用map
,感觉好厉害!Σ(゚∀゚ノ)ノ