Codeforces475D

Portal

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,感觉好厉害!Σ(゚∀゚ノ)ノ

原文地址:https://www.cnblogs.com/VisJiao/p/8494287.html