• LG4213 【模板】杜教筛(Sum)和 BZOJ4916 神犇和蒟蒻


    P4213 【模板】杜教筛(Sum)

    题目描述

    给定一个正整数$N(Nle2^{31}-1)$

    $$ans_1=sum_{i=1}^nvarphi(i)$$

    $$ans_2=sum_{i=1}^n mu(i)$$

    输入输出格式

    输入格式:

    一共T+1行 第1行为数据组数T(T<=10) 第2~T+1行每行一个非负整数N,代表一组询问

    输出格式:

    一共T行,每行两个用空格分隔的数ans1,ans2

    输入输出样例

    输入样例#1: 复制
    6
    1
    2
    8
    13
    30
    2333
    输出样例#1: 复制
    1 1
    2 0
    22 -2
    58 -3
    278 -3
    1655470 2

    AcFirmament的题解

    杜教筛模板

    杜教筛是用来干蛤的呢?

    它可以在非线性时间内求积性函数前缀和。

    前置知识

    积性函数

    积性函数:对于任意互质的整数 $a,b$ 有 $f(ab)=f(a)f(b)$ 则称 $f(x)$ 的数论函数。

    完全积性函数:对于任意整数 $a,b$ 有 $f(ab)=f(a)f(b)$ 的数论函数。

    • 常见的积性函数:$varphi,mu,sigma,d$
    • 常见的完全积性函数:$epsilon,I,id$

    这里特殊解释一下 $epsilon,I,id$ 分别是什么意思: $epsilon(n) = [n=1], I(n) = 1, id(n) = n$

    狄利克雷卷积

    设 $f, g$ 是两个数论函数,它们的狄利克雷卷积卷积是:$(f*g)(n) = sum limits _{d | n} f(d) g(frac{n}{d})$

    性质:满足交换律,结合律

    单位元:$epsilon$ (即 $f*epsilon=f$)

    结合狄利克雷卷积得到的几个性质:

    1. $mu * I = epsilon$
    2. $varphi * I = id$
    3. $mu * id = varphi$

    莫比乌斯反演

    若 $$g(n) = sumlimits_{d|n}f(d)$$

    则 $$f(n)=sumlimits_{d|n}mu(d)g(frac{n}{d})$$

    证明:这里需要用到前面提到的性质:$mu * I = epsilon$

    给出的条件等价于 $g=f * I$

    所以 $g*mu=f*I*mu=f*epsilon=f$ 即 $g * mu = f$ 即 结论。


    杜教筛

    设现在要求积性函数 $f$ 的前缀和, 设 $sum limits_{i=1}^{n} f(i) = S(n)$。

    再找一个积性函数 $g$ ,则考虑它们的狄利克雷卷积的前缀和

    $$sumlimits_{i=1}^{n}(f*g)(i)$$

    $$egin{aligned} &= sumlimits_{i=1}^{n} sum limits _{d|i} f(d)g(frac{i}{d}) \ &= sum limits _{d=1}^{n} g(d)sumlimits _{i=1}^{lfloor frac{n}{d} floor } f(i) \ &= sum limits _{d=1}^{n} g(d) S(lfloor frac{n}{d} floor) end{aligned}$$

    其中得到第一行是根据狄利克雷卷积的定义。

    得到第二行则是先枚举 $d$ 提出 $g$ 。

    得到第三行则是把 $sumlimits _{i=1}^{lfloor frac{n}{d} floor } f(i) $ 替换为 $S(lfloor frac{n}{d} floor) $

    接着考虑 $g(1)S(n)$ 等于什么。

    可以发现,他就等于 $$ sum limits _{i=1}^{n} g(i) S(lfloor frac{n}{i} floor) - sum limits _{i=2}^{n} g(i) S(lfloor frac{n}{i} floor)$$

    (可以理解成从1开始的前缀和减去从2开始的前缀和就是第一项)

    前面这个式子$sum limits _{i=1}^{n} g(i) S(lfloor frac{n}{i} floor)$ ,根据刚才的推导,他就等于 $sumlimits_{i=1}^{n}(f*g)(i)$

    所以得到杜教筛的核心式子:

    $$g(1)S(n)=sumlimits_{i=1}^{n}(f*g)(i) - sum limits _{i=2}^{n} g(i) S(lfloor frac{n}{i} floor)$$

    得到这个式子之后有什么用呢?

    现在如果可以找到一个合适的积性函数 $g$ ,使得可以快速算出 $sumlimits_{i=1}^{n}(f*g)(i)$ 和 $g$ 的前缀和,便可以用数论分块递归地求解。

    代码按照理解大概可以写成这样(默认 lllong long) (可以理解成一个伪代码。。就是一个思路的框架)

    ll GetSum(int n) { // 算 f 前缀和的函数
      ll ans = f_g_sum(n); // 算 f * g 的前缀和
      // 以下这个 for 循环是数论分块
      for(ll l = 2, r; l <= n; l = r + 1) { // 注意从 2 开始
        r = (n / (n / l)); 
        ans -= (g_sum(r) - g_sum(l - 1)) * GetSum(n / l);
        // g_sum 是 g 的前缀和
        // 递归 GetSum 求解
      } return ans; 
    }

    这个代码的复杂度是 $O(n^{frac{3}{4}})$,证明如下:

    设求出 $S(n)$ 的复杂度是 $T(n)$ ,要求出 $S(n)$ 需要求出 $sqrt n$ 个 $S (lfloor frac{n}{i} floor)$ 的值,结合数论分块的复杂度 $O(sqrt n)$ 可得: $$T(n) = sumlimits_{i=1}^{sqrt n} O(sqrt i) + O(sqrt {frac{n}{i}})=O(n^{frac{3}{4}})$$

    还可以进一步优化杜教筛,即先线性筛出前 $m$ 个答案,之后再用杜教筛。这个优化之后的复杂度是:

    $$T(n) = sumlimits_{i=1}^{lfloor frac{n}{m} floor} sqrt frac{n}{i} = O({frac{n}{sqrt m}})$$

    当 $m = n ^ {frac{2}{3}}$ 时,$T(n) = O(n^{frac{2}{3}})$

    可以使用哈希表来存下已经求过的答案,也可以不用。

    考虑到上面的求和过程中出现的都是 $lfloor frac{n}{i} floor $ 。开一个大小为两倍 $sqrt n$ 的数组 $dp$ 记录答案。如果现在需要求出 GetSum(x) ,若 $x leq sqrt n$ ,返回 dp[x] ,否则返回 dp[sqrt n + n / i] 即可。这样可以省去哈希表的复杂度。



    实战演练

    再挂一次核(tao)心(lu)式,全都要靠它: $$g(1)S(n)=sumlimits_{i=1}^{n}(f*g)(i) - sum limits _{i=2}^{n} g(i) S(lfloor frac{n}{i} floor)$$

    它的关键就是要找到合适的 $g$ 使得这个东西可以快速地算。

    理论知识大概就这么多,接下来看几个例子:

    (1) $mu$ 的前缀和

    考虑到莫比乌斯函数的性质 $mu * I = epsilon$ ,自然想到取 $f=mu,g=I,f*g=epsilon$ 。

    其中 $I$ 的前缀和和 $epsilon$ 的前缀和都弱到爆了。。

    所以就轻松的解决了。

    杜教筛代码:

    inline ll GetSumu(int n) {
      if(n <= N) return sumu[n]; // sumu是提前筛好的前缀和
      if(Smu[n]) return Smu[n]; // 记忆化
      ll ret = 1ll; // 单位元的前缀和就是 1
      for(int l = 2, r; l <= n; l = r + 1) {
        r = n / (n / l); ret -= (r - l + 1) * GetSumu(n / l);
        // (r - l + 1) 就是 I 在 [l, r] 的和
      } return Smu[n] = ret; // 记忆化
    }

    (2) $varphi$ 的前缀和

    考虑到 $varphi$ 的性质 $varphi * I = id$,取 $f = varphi, g = I, f * g = id$

    $f * g$ 即 $id$ 的前缀和为 $frac{n * (n+1)}{2}$

    杜教筛代码:

    inline ll GetSphi(int n) {
      if(n <= N) return sump[n]; // 提前筛好的
      if(Sphi[n]) return Sphi[n]; // 记忆化
      ll ret = 1ll * n * (n + 1) / 2; // f * g = id 的前缀和
      for(int l = 2, r; l <= n; l = r + 1) {
        r = n / (n / l); ret -= (r - l + 1) * GetSphi(n / l);
        // 同上,因为两个的 g 都是 I 
      } return Sphi[n] = ret; // 记忆化
    }

    (1) & (2) 就是杜教筛模板 luogu p4213

    (3) (综合)$sumlimits_{i=1}^{n}varphi(i) cdot i$

    令 $f = varphi cdot id, g = id$, 考虑迪利克雷卷积的形式得到 $(f*g)(n)=sum limits _{d|n} (varphi(d) cdot d) cdot (frac{n}{d}) = n sumlimits_{d|n}varphi(d)=n^2$

    即 $(f * g)(i) = i^2$

    这样就可以快速求得 $(f*g)(i)$ 的前缀和 $frac{n(n+1)(2n+1)}{6}$

    就可以了。



    题目

    先推荐洛谷模板题

    还有一题 luoguP3768 简单的数学题,这道题推完式子可以用杜教筛来求 $varphi(i)i^2$ 的前缀和,和 $varphi(i)i$ 所差无几。

    51nod 上也有很多杜教筛的题目,放几个:

    • 51nod 1244
    • 51nod 1237
    • 51nod 1238
    • 51nod 1239
    • 51nod 1220
    • ...

    然后就是卡常优化,卡着开空间,unordered_map乱搞。

    #include<bits/stdc++.h>
    #define il inline
    #define co const
    template<class T>T read(){
        T data=0,w=1;char ch=getchar();
        for(;!isdigit(ch);ch=getchar())if(ch=='-') w=-w;
        for(;isdigit(ch);ch=getchar()) data=data*10+ch-'0';
        return data*w;
    }
    template<class T>il T read(T&x) {return x=read<T>();}
    typedef long long LL;
    using namespace std;
    
    co int N=6500000;
    int pri[N],tot;
    LL mu[N],phi[N];
    void sieve(){
    	pri[1]=mu[1]=phi[1]=1;
    	for(int i=2;i<N;++i){
    		if(!pri[i]) pri[++tot]=i,mu[i]=-1,phi[i]=i-1;
    		for(int j=1;j<=tot&&i*pri[j]<N;++j){
    			pri[i*pri[j]]=1,mu[i*pri[j]]=-mu[i],phi[i*pri[j]]=phi[i]*phi[pri[j]];
    			if(i%pri[j]==0){
    				mu[i*pri[j]]=0,phi[i*pri[j]]=phi[i]*pri[j];
    				break;
    			}
    		}
    	}
    	for(int i=2;i<N;++i) mu[i]+=mu[i-1],phi[i]+=phi[i-1];
    }
    unordered_map<int,LL> smu,sphi;
    LL sum_mu(int n){
    	if(n<N) return mu[n];
    	if(smu.count(n)) return smu[n];
    	LL ans=1;
    	for(unsigned l=2,r;l<=n;l=r+1){ // 2^32-1
    		r=n/(n/l);
    		ans-=(r-l+1)*sum_mu(n/l);
    	}
    	return smu[n]=ans;
    }
    LL sum_phi(int n){
    	if(n<N) return phi[n];
    	if(sphi.count(n)) return sphi[n];
    	LL ans=(LL)n*(n+1)/2;
    	for(unsigned l=2,r;l<=n;l=r+1){
    		r=n/(n/l);
    		ans-=(r-l+1)*sum_phi(n/l);
    	}
    	return sphi[n]=ans;
    }
    int main(){
    	cerr<<(sizeof(pri)+sizeof(phi)+sizeof(mu))/1024.0/1024<<endl;
    	sieve();
    	for(int t=read<int>();t--;){
    		int n=read<int>();
    		printf("%lld %lld
    ",sum_phi(n),sum_mu(n));
    	}
    	return 0;
    }
    

    jklover:整除是可以结合的,所以才有开数组那一说。

    4916: 神犇和蒟蒻

    Time Limit: 10 Sec  Memory Limit: 512 MB
    Submit: 1085  Solved: 589
    [Submit][Status][Discuss]

    Description

    很久很久以前,有一只神犇叫yzy;
    很久很久之后,有一只蒟蒻叫lty;

    Input

    请你读入一个整数N;1<=N<=1E9,A、B模1E9+7;

    Output

    请你输出一个整数A=sum_{i=1}^N{mu (i^2)};
    请你输出一个整数B=sum_{i=1}^N{varphi (i^2)};

    Sample Input

    1

    Sample Output

    1
    1

    HINT

    Source

    [Submit][Status][Discuss]
    
    HOME Back

    题解

    第一问答案就是1。

    对于第二问,(varphi(n^2))用定义式写开,发现就是(nvarphi(n))。然后就是上面那份题解里面的(3)综合

    (varphicdot id * id=id^2)

  • 相关阅读:
    C# get和set
    动手学pytorch-优化算法
    动手学pytorch-梯度下降
    动手学pytorch-凸优化
    动手学pytorch-Batch Norm
    动手学pytorch-经典卷积神经网络模型
    动手学pytorch-卷积神经网络基础
    动手学pytorch-Transformer代码实现
    动手学pytorch-机器翻译
    动手学pytorch-循环神经网络进阶
  • 原文地址:https://www.cnblogs.com/autoint/p/dujiao_sieve.html
Copyright © 2020-2023  润新知