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));
        }
    }
    
  • 相关阅读:
    [ThreadStatic] dosen't work with instance fields
    Java XxlJob 必知必会<续篇>
    Python 数据可视化神器—Pyecharts
    PICT 生成正交测试用例教程
    Hive 分桶表核心知识点
    Python + Flask 实现接口接收内存信息
    数据工程师:必备的 Hive 安装&交互方式技能
    JvmSandboxRepeater 配置修改详解
    JavaDubbo 接口测试
    Hadoop + Hive 数据仓库原理与架构
  • 原文地址:https://www.cnblogs.com/victorique/p/10422968.html
Copyright © 2011-2022 走看看