zoukankan      html  css  js  c++  java
  • 杜教筛 [学习笔记]【更新中】

    杜教筛

    嘟嘟嘟


    tangjz orz

    jiry_2 orz

    任之洲 2016国家队论文 orz


    概述

    前置技能:莫比乌斯反演

    可以在(O(frac{3}{4}))(O(frac{2}{3}))复杂度完成数论函数(前缀和)的计算


    一般形式

    数论函数(f(n)),求

    [S(n) = sum_{i=1}^n f(i) ]

    对于任意数论函数(g(n)),均满足

    [sum_{i=1}^n (f*g)(i) = sum_{i=1}^n g(i)S(lfloor frac{n}{i} floor) ]

    (Proof.)

    [egin{align*} sum_{i=1}^n (f*g)(i) &= sum_{i=1}^n sum_{dmid i} f(d) g(frac{i}{d}) \ 先枚举几倍再枚举约数 \ &= sum_{i=1}^n g(i) sum_{d=1}^{lfloor frac{n}{d} floor}f(d) \ &= sum_{i=1}^n g(i) S(lfloor frac{n}{d} floor) end{align*} ]

    • 注意枚举几倍的技巧:对于i*j=k,我们枚举了i和j

    这样的话可以得到

    [g(1)S(n) = sum_{i=1}^n (f*g)(i) - sum_{i=2}^n g(i)S(lfloor frac{n}{i} floor) ]

    通常情况(g(1)=1)

    这个柿子可以整除分块

    找到合适的(g)

    如果能通过狄利克雷卷积构造一个更好计算前缀和的函数,且用于卷积的另一个函数也易计算,则可以简化计算过程


    时间复杂度

    假设计算出(ϕ(n))的复杂度为(T(n)),则有(T(n)=O(sqrt{n})+sum_{i=1}^{sqrt{n}}{T(i)+T(frac{n}{i})})这里只展开一层就可以了,更深层的复杂度是高阶小量,所以有(T(n)=sum_{i=1}^{sqrt{n}}{O(sqrt{i})+O(sqrt{frac{n}{i}})}=O(n^frac{3}{4}))

    预处理前k个的答案后,(T(n)=sum_{i=1}^{frac{n}{k}}{sqrt{frac{n}{i}}}=O(frac{n}{sqrt{k}})),(k=O(n^frac{2}{3}))时最优(T(n)=O(n^frac{2}{3}))


    基本形式

    • 基本形式只是为了简化思考,用一般形式找函数卷上均可以完成
    • 就是说没大有用啦

    一. 数论函数(f(n)),完全积性函数(g(n)),求

    [S(n)=sum_{i=1}^n(f cdot g)(i) ]

    此时满足

    [sum_{i=1}^n {((f*1) cdot g)(i)} = sum_{i=1}^n g(i)S(lfloor frac{n}{i} floor) ]

    (Proof.)

    [egin{align*} sum_{i=1}^n {((f*1) cdot g)(i)} &= sum_{i=1}^n g(i) sum_{dmid i} f(d) \ 先枚举几倍 \ &= sum_{i=1}^n sum_{d=1}^{lfloor frac{n}{i} floor} f(d) g(id) \ 因为g(id)=g(i)cdot g(d) \ &= sum_{i=1}^n g(i) S(lfloor frac{n}{d} floor) end{align*} ]

    也就是说这种情况我们只要让另一个函数卷(1),完全积性函数直接当做我们找的函数。


    二. 待填坑




    简单的栗子

    都是卷上(1)

    一. 求(mu)的前缀和

    [mu * 1 = epsilon \ S(n) = 1 - sum_{i=2}^n S(lfloor frac{n}{i} floor) ]

    二. 求(varphi)的前缀和

    [phi * 1 = id \ S(n) = frac{n(n+1)}{2} - sum_{i=2}^n S(lfloor frac{n}{i} floor) ]


    代码实现

    使用记忆化搜索和哈希表

    //这是phi前缀和。首先要线性筛预处理一部分。
    namespace ha {
    	const int p = 1001001;
    	struct meow{int ne; ll val, r;} e[3000];
    	int cnt=1, h[p];
    	inline void insert(ll x, ll val) {
    		ll u = x % p;
    		for(int i=h[u];i;i=e[i].ne) if(e[i].r == x) return;
    		e[++cnt] = (meow){h[u], val, x}; h[u] = cnt;
    	}
    	inline ll quer(ll x) {
    		ll u = x % p;
    		for(int i=h[u];i;i=e[i].ne) if(e[i].r == x) return e[i].val;
    		return -1;
    	}
    } using ha::insert; using ha::quer;
    
    ll dj_sieve(ll n) {
    	if(n <= U) return phi[n];
    	if(quer(n) != -1) return quer(n);
    	ll ans = n%mo * ((n+1) %mo) %mo * inv2 %mo, r;
    	for(ll i=2; i<=n; i=r+1) {
    		r = n/(n/i);
    		mod(ans -= dj_sieve(n/i) * ((r-i+1) %mo) %mo);
    	}
    	insert(n, ans);
    	return ans;
    }
    

    栗子

    一. 求 (sum_{i=1}^n sum_{j=1}^n lcm(i, j))

    有两类做法。

    一.变成积性函数求和的形式

    首先进行化简

    [egin{align*} A(n) &= sum_{i=1}^nlcm(i,n)=n sum_{i=1}^nfrac{i}{(n,i)} \ &=n sum_{dmid n}sum_{i=1}^{lfloor frac{n}{d} floor}i[(frac{n}{d}, i)=1] \ &= frac{1}{2} n (1 + sum_{dmid n}d cdot varphi(d)) end{align*} ]

    (ans = 2*A(n) - sum_{i=1}^n)

    就是(f(n) = nsum_{dmid n}d cdot varphi(d)= id cdot ((id cdot varphi) * 1))的前缀和。

    • 其中重要的一步在于使用欧拉函数的性质代入,而不是使用莫比乌斯函数代入

    这里又有两种做法。


    法一:

    [egin{align*} ans &= sum_{i=1}^n i sum_{dmid i}d cdot varphi(d) \ 先枚举几倍 \ &= sum_{i=1}^n i sum_{d=1}^{lfloor frac{n}{i} floor} d^2 cdot varphi(d) end{align*} ]

    后面是可以整除分块的形式,我们只要处理出(i^2 cdot varphi(i)=(id^2 cdot varphi)(i))的前缀和(S(n))就行了

    (id^2)是完全积性函数,满足杜教筛基本形式一,单独让(varphi)卷上(1),也变成(id)了。

    或者直接卷上(id^2),效果相同

    [S(n) = sum_{i=1}^n i^3 - sum_{i=2}^n i^2 S(lfloor frac{n}{i} floor) ]

    其中(sum_{i=1}^{n} i^3 = (frac{n(n+1)}{2})^2, sum_{i=1}^{n} i^2 = frac{n(n+1)(2n+1)}{6})


    杜教筛的过程中计算了所有(S(lfloor frac{n}{i} floor)),可以和整除分块配合食用


    法二:

    把f外面的n拿进来

    直接计算(f(n) = id cdot ((id cdot varphi) * 1) = (id^2 cdot varphi) * id)的前缀和。

    卷上(id^2)

    [((id^2 cdot varphi) * id^2) * id = id^3 * id \ S(n) = sum_{i=1}^n (id^3 * id)(i) - sum_{i=2}^n i^2 S(lfloor frac{n}{i} floor) ]

    前部分的前缀和可以用整除分块解决

    使用线性筛直接预处理S的前(n^{frac{2}{3}})项,只要预处理((id cdot varphi) * 1 = sum_{dmid n}d cdot varphi(d))就行了,因为是点乘比较好做。(p^c)的形式处理出来,剩下的保留最小质因子的幂次然后用积性。


    二.使用莫比乌斯反演的套路

    abclzr的博客发现了这种做法:

    套路推♂倒之后得到的柿子是:

    [sum_{i=1}^n frac{lfloor frac{n}{i} floor(lfloor frac{n}{i} floor+1)}{2} frac{lfloor frac{m}{i} floor(lfloor frac{m}{i} floor+1)}{2} i sum_{dmid i} dcdot mu(d) ]

    我们只需用杜教筛计算(i sum_{dmid i} dcdot mu(d))的前缀和,然后与整除分块配合就行了(因为每次块的r的值也是一个(frac{n}{i})的值)

    就是要计算(f=(id^2 cdot mu) * id)的前缀和,卷上(id^2)后变成(f(n) = nsum_{dmid n}[d=1]=n)

    上杜教筛就行了

    预处理的话,可以用线性筛预处理(f'=sum_{dmid n}dcdot mu(d)),发现(f'=prod_i(1-p_i))

    这样做的好处是可以处理n和m的情况




    总结

    找到一个函数卷上方便求

    莫比乌斯反演的题目一般是约数和倍数互换 令D=de,这里一般是约数与几倍互换

    TA:

    对于d,e,D=d*e 三项贡献的这种,可以枚举D将其化为狄利克雷卷积,也可以枚举d和e化成带下取整的式子;一般来讲前者往往易于预处理,可以应付多组询问,后者则在单次询问中有优秀表现。

  • 相关阅读:
    struts2第一天——入门和基本操作
    eclipse各种小图标含义
    复制web项目时注意修改web项目名
    AndroidCityPicker仿IOS选择效果
    每日五题(Spring)
    block-循环引用
    给EasyUi的Form加入自己主动填充部分输入框的方法
    智能停车O2O 独角兽初现:“ETCP停车”获5000万美金A轮融资
    解决移动端页面滚动后不触发touchend事件
    《从零開始学Swift》学习笔记(Day 61)——Core Foundation框架之内存管理
  • 原文地址:https://www.cnblogs.com/candy99/p/dj_s.html
Copyright © 2011-2022 走看看