快来踩爆这个蒟蒻吧|

Little_corn

园龄:1年1个月粉丝:10关注:17

2024-04-25 13:25阅读: 6评论: 0推荐: 0

数论函数小记

摘自 command_block 大佬的笔记

基础:

  • 积性函数:若当 (i,j)=1f(i×j)=f(i)×f(j),则称 f 为积性函数。

  • 完全积性函数:若当 f(i×j)=f(i)×f(j) , 则称 f 为完全积性函数。

  • I(n) : 值恒为 1 的函数。

  • e(n) : 当 n=1 时值为 1,否则为 0 。

  • id(n)id(n)=n

  • tips:这些都为完全积性函数。

狄利克雷卷积:

给定两个数论函数 f,g ,则这两个函数的狄利克雷卷积 (fg) 定义为:

(fg)(n)=d|nf(d)g(nd)

特殊的, 我们一个函数 f 的逆 f1 定义为: ff1=e

莫比乌斯函数:

  • 引理 1:

    若两个函数 f,g 的卷积为积性函数,且 g 为积性函数,则 f 也是积性函数

我们定义 μ 为一个逆为 I积性函数,既满足 μI=e,接下来我们推一下它的定义。

显然 μ(1)=1

  • tips: 观察一个积性函数,可以先从 pk 的情形入手。

μ(pk):k=0 时,函数值为 1。

由卷积的定义得: μ(pk)=μ(pk)+μ(pk1)+...+μ(p0)

即为: μ(pk)+μ(pk1)+...+μ(p0)=0

又因为 μ积性函数,所以 μ(n)=i=1cntpμ(pici)

则可以得到 μ 的定义了:

n 有一个次数大于一的质因子时,μ(n)=0

否则 μ(n)=(1)cntp.

一些常见的结论:

  • 卷积式:

    μI=e

    Iφ=ididμ=φ

    d=II (d 是一个数的约数个数)

    F=fIf=μF (重要!!)

  • 函数运算:

    μ(ij)=μ(i)μ(j)[ij]

    d(ij)=x|iy|j[xy]

    i=1nd(i)=i=1nni

莫比乌斯反演:

十分灵活,主要基于上面的柿子和一些奇怪结论。

看一道例题 P2257 YY的GCD

题目就是让你求:

i=1nj=1m[(i,j)=prime]

=pprimeni=1nj=1m[(i,j)=p]

(看起来很废话,但是也是重要的)

i,j 都除掉 p:

pprimeni=1n/pj=1m/p[(i,j)=1]

代入 μI=e

pprimeni=1n/pj=1m/pd=1m/pμ(d)[d|i][d|j]

μ 提到前面:

pprimend=1m/pμ(d)i=1n/pj=1m/p[d|i][d|j]

整理一下:

pprimend=1m/pμ(d)ndpmdp

  • tips: 遇到乘积式需要考虑代换掉并变为整除形式。

还是不好做,发现 dp 为乘积式很不好,令 k=dp

k=1npprimep|kμ(kp)nkmk

  • tips: 发现求和式性质不明显时,可以考虑交换枚举变量或交换求和式顺序。

注意到这个柿子只有 μ 项与 p 相关,于是交换求和式顺序。

k=1nnkmkpprimep|kμ(kp)

f(x) 为后面那一坨柿子,用埃氏筛即可预处理出来。

接着使用整除分块技巧计算前面的柿子即可。

code:

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e7 + 10;
int prime[N], tot, mu[N], g[N];
bool isprime[N];
void init(){
for(int i = 2; i < N; i++) isprime[i] = true;
isprime[1] = false, mu[1] = 1;
for(int i = 2; i < N; i++){
if(isprime[i]) prime[++tot] = i, mu[i] = -1;
for(int j = 1; j <= tot && i * prime[j] < N; j++){
isprime[i * prime[j]] = false;
if(i % prime[j] == 0) break;
mu[i * prime[j]] = -mu[i];
}
}
for(int i = 1; i <= tot; i++){
for(int j = prime[i]; j < N; j += prime[i]){
g[j] += mu[j / prime[i]];
}
}
for(int i = 2; i < N; i++) g[i] += g[i - 1];
}
int H(int n, int m){
int ret = 0, l = 1, r, siz = min(n, m);
while(l <= siz){
r = min(siz, min(n / (n / l), m / (m / l)));
ret += (g[r] - g[l - 1]) * (n / l) * (m / l);
l = r + 1;
}
return ret;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
init(); int T; cin >> T;
while(T--){
int n, m;
cin >> n >> m;
cout << H(n, m) << "\n";
}
return 0;
}

还有另一种反演形式:

i=1nj=1mf(gcd(i,j))

向上面一样的推法可以得出这个柿子等于:

x=1nnxmxd|xμ(d)f(xd)

f 是积性函数,则可以欧拉筛,否则埃氏筛即可。

例题:P3312 [SDOI2014] 数表

杜教筛:

杜教筛是一种高效计算一类数论函数前缀和的筛法。

杜教筛的核心是构造两个合适的数论函数,使得 h=fgf 为待求函数),而且 h,g 的前缀和易求。

杜教筛公式:

我们令 S(n)=i=1nf(i)

于是有:

i=1nh(i)=i=1nd|ig(d)f(id)

做一个小小的变换得到:

i=1nh(i)=d=1ng(d)d|inf(id)

进行化简:

i=1nh(i)=d=1ng(d)i=1ndf(i)

整理后有:

i=1nh(i)=d=1ng(d)S(nd)

S(n) 拆出来:

g(1)S(n)=i=1nh(i)i=2ng(i)S(ni)

这就是杜教筛公式了。

实现:

首先我们算出 nf 的前缀和。

对于一个 S(n),我们对它做整除分块并递归计算 S(nl)

可以证明时间复杂度为 O(n23)(然而我太菜了并不会呜呜呜)。

例题

本文作者:Little_corn

本文链接:https://www.cnblogs.com/little-corn/p/18157526

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   Little_corn  阅读(6)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起