zoukankan      html  css  js  c++  java
  • BZOJ3944: Sum(杜教筛模板)

    BZOJ3944: Sum(杜教筛模板)


    题面描述

    传送门

    题目分析

    (sum_{i=1}^{n}mu(i))(sum_{i=1}^{n}varphi(i))

    数据范围线性不可做。

    需要使用杜教筛。

    杜教筛可以在非线性时间里求出一个积性函数的前缀和。

    借这里先写一些杜教筛内容。。。或许以后会补总结(雾

    最开始扔积性函数:

    1. (mu(n)),莫比乌斯函数
    2. (phi(n)),欧拉函数。
    3. (d(n)),约数个数。
    4. (sigma(n)),约数和函数。
    5. (epsilon(n)),元函数,其值为(epsilon(n)=[n=1])
    6. (id(n)),单位函数,(id(n)=n)
    7. (I(n)),恒等函数,(I(n)=1)

    先放狄利克雷卷积的式子:

    假设我们现在有两个数论函数(f,g),则这两个函数的卷积是((f*g)(n)=sum_{dmid n}f(d)·g(frac{n}{d}))后面的括号表示范围,一般不写的时候可以默认其为(n)

    可以推出狄利克雷卷积满足以下运算律

    1. 交换律:((f∗g=g∗f))
    2. 结合律:(((f∗g)∗h=f∗(g∗h)))
    3. 分配律:(((f+g)∗h=f∗h+g∗h))

    可以类比乘法运算律记忆。

    那么我们可以开始搞杜教筛了。

    现在我们要求一个积性函数(f)的前缀和,也就是(sum_{i=1}^{n}f(i))

    我们尝试构造两个积性函数使(h=f*g)

    那么我们求一下(sum_{i=1}^{n}h(i))

    先记(Sum(n))(sum_{i=1}^{n}f(i))
    则:

    [sum_{i=1}^{n}h(i)=sum_{i=1}^{n}sum_{dmid i}g(d)f(frac{i}{d}) ]

    然后明显可以反过来枚举。

    [→sum_{d=1}^{n}g(d)sum_{dmid i}f(frac{i}{d}) ]

    改成枚举(frac{i}{d})

    [→sum_{d=1}^{n}g(d)sum_{i=1}^{lfloorfrac{n}{d} floor}f(i)=sum_{d=1}^{n}g(d)S(lfloorfrac{n}{d} floor) ]

    然后把式子的第一项提出来,整个代回去。

    [sum_{i=1}^{n}h(i)=g(1)·S(n)+sum_{d=2}^{n}g(d)·S(lfloorfrac{n}{d} floor) ]

    移项

    [g(1)·S(n)=sum_{i=1}^{n}h(i)-sum_{d=2}^{n}g(d)·S(lfloorfrac{n}{d} floor) ]

    这样(g(1))明显为(1),所以这个式子就很明显了,只要(h(i))的前缀和好求那么这个式子就可以在非线性时间里求出来了。

    因为(h=f*g)我们换个形式表示上面的式子。

    [→g(1)·S(n)=sum_{i=1}^{n}(f*g)(i)-sum_{d=2}^{n}g(d)·S(lfloorfrac{n}{d} floor) ]

    所以只要找到一个合适的(g)就行了。

    看个例子,我们这个题要求啥来着,(sum_{i=1}^{n}mu(i))(sum_{i=1}^{n}varphi(i))

    先看第一个。

    就不推了,根据上面那个把(f)换成(mu)直接代到最后面。

    那么应该怎么给(g)取值呢,我们可以简明扼要的先看一下那一项变成什么了。

    [→sum_{i=1}^{n}(mu*g)(i) ]

    有一个好消息,我们知道(mu*I=epsilon)。那么可以把上面的式子看成

    [sum_{i=1}^{n}(mu*I)(i)=sum_{i=1}^{n}epsilon(i) ]

    元函数的前缀和就非常好求,就是(1),所以我们求的答案

    [S(n)=1-sum_{d=2}^{n}g(d)·S(lfloorfrac{n}{d} floor) ]

    再看第二个,我们还是相同的直接把(varphi)代到最后面去。

    则我们有式子

    [sum_{i=1}^{n}(varphi*g)(i) ]

    思考一下,我们记得欧拉函数有个有趣的性质(sum_{d|n}varphi(d)=n)

    我们把它用卷积的形式表达,就是(varphi*I=id)

    带入刚才的式子里面。

    [sum_{i=1}^{n}(varphi*g)(i)=sum_{i=1}^{n}id(i) ]

    明显的小高斯5岁就会的那个数列求和。。。

    这个东西是(n·(n+1)/2)应该都知道。。。

    然后代码实现的时候,可以先筛出根号范围内的答案,然后递归处理记忆化搜索。

    由于需要储存下标非常大的值,所以需要使用哈希或者偷懒使用unordered_map,不要用map,会多一个log。

    下面代码实测BZOJ可过,注意少开long long

    是代码呢

    #include <bits/stdc++.h>
    #include <tr1/unordered_map>
    using namespace std;
    const int MAXN=4e6+7;
    const int M=4e6;
    #define ll long long 
    bool vis[MAXN];
    int mu[MAXN],sum1[MAXN];
    ll phi[MAXN],sum2[MAXN];
    int cnt,prime[MAXN];
    tr1::unordered_map<ll,ll> w1;
    tr1::unordered_map<int,short> w;
    inline void get(int N)
    {
        phi[1]=mu[1]=1;
        for(int i=2;i<=N;i++){
            if(!vis[i]){
                prime[++prime[0]]=i;
                mu[i]=-1;
                phi[i]=i-1;
            }
            for(int j=1;j<=prime[0];j++){
                if(i*prime[j]>N) break;
                vis[i*prime[j]]=1;
                if(i%prime[j]==0){
                    phi[i*prime[j]]=phi[i]*prime[j];
                    break;
                } else mu[i*prime[j]]=-mu[i],phi[i*prime[j]]=phi[i]*(prime[j]-1);
            }
        }
        for(int i=1;i<=N;i++) sum1[i]=sum1[i-1]+mu[i],sum2[i]=sum2[i-1]+phi[i];
    }
    int djsmu(int x)
    {
        if(x<=M) return sum1[x];
        if(w[x]) return w[x];
        int ans=1;
        for(int l=2,r;l<=x;l=r+1){
            if(r==2147483647) break;
            r=x/(x/l);
            ans-=(r-l+1)*djsmu(x/l);
        }
        return w[x]=ans;
    }
    ll djsphi(int x)
    {
        if(x<=M) return sum2[x];
        if(w1[x]) return w1[x];
        ll ans=1ll*x*(1ll*x+1)/2;
        for(int l=2,r;l<=x&&l>=0;l=r+1){
            if(r==2147483647) break;
            r=x/(x/l);
            ans-=1ll*(r-l+1)*djsphi(x/l);
        }
        return w1[x]=ans;
    }
    inline int read()
    {
        int x=0,c=1;
        char ch=' ';
        while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
        while(ch=='-')c*=-1,ch=getchar();
        while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
        return x*c;
    }
    int main()
    {
        int T=read();
        get(M);
        while(T--){
            int n;
            n=read();
            printf("%lld %d
    ", djsphi(n),djsmu(n));
        }
    }
    
  • 相关阅读:
    ant 软件包不存在报错
    在 Internet Explorer 中使用 Windows 窗体控件
    智能客户端
    Back to the Future with Smart Clients
    "Automation 服务器不能创建对象" 的解决方案
    Top 10 Reasons for Developers to Create Smart Clients
    Updater Application Block for .NET
    Smart Client Application Model and the .NET Framework 1.1
    Security and Versioning Models in the Windows Forms Engine Help You Create and Deploy Smart Clients
    智能客户端技术总结(二)
  • 原文地址:https://www.cnblogs.com/victorique/p/10422968.html
Copyright © 2011-2022 走看看