Codeforces475D - CGCDSSQ

Portal

Description

给出长度为\(n(n\leq10^5)\)的序列\(\{a_n\}\),给出\(q(q\leq3\times10^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\n",ans[x]);
    }
    return 0;
}

P.S.

“以\(a_i\)为左端点的区间\(gcd\),最多只有\(loga_i\)种取值”感觉这个结论还蛮有用的。
第一次用map,感觉好厉害!Σ(゚∀゚ノ)ノ

posted @ 2018-03-02 17:22  VisJiao  阅读(143)  评论(0编辑  收藏  举报