听起来很 nb,很有名但比较难学的一个算法类型。然而确实很 nb。
我竟然在学 ymx 一年半前就学过的东西。
1. 反演的本质与第一反演公式
1.1. 什么是反演
反演是通过用 \(f\) 表示 \(g\) 的方法求出如何用 \(g\) 表示 \(f\)。
如果我们已知 \(g(n)\) 以及它如何用 \(f\) 表示:\(g(n)=\sum_{i=1}^nc_{n,i}f(i)\),从而反推出用 \(g\) 表示 \(f\) 的方法:\(f(n)=\sum_{i=1}^nd_{n,i}g(i)\),那么就算 \(f\) 无法直接求,也可以通过 \(g(i)\) 的值推出 \(f(n)\)。
上面二式互为反演公式。
1.2. 第一反演公式
这里直接给出定理:如果 \(n\) 次多项式 \(p\),\(q\) 分别满足以下公式:\(p_n(x)=\sum_{i=1}^n c_{n,i}q_i(x)\),\(q_n(x)=\sum_{i=1}^n d_{n,i} p_i(x)\),且 \(c_{j,j}\neq0,d_{j,j}\neq 0\),那么
简单来说,如果将 \(c,d\) 写成矩阵的形式 \(\mathbf{C},\mathbf{D}\),那么它显然是下三角矩阵,同时有 \(\mathbf{CD}=\mathbf{I}\)。因为如果将 \(p,q\) 写成列向量 \(\mathbf{p},\mathbf{q}\),那么条件中的两个公式可以表示为 \(\mathbf{p}=\mathbf{Cq}\),\(\mathbf{q}=\mathbf{Dp}\),即 \(\mathbf{p}=\mathbf{CDp}\),得证。由于 \(\mathbf{C}\) 与 \(\mathbf{D}\) 互逆,所以它们的行列式不为 \(0\),而因为它们是下三角矩阵,因此一定有对角线元素不为 \(0\),这也就解释了为什么 \(c_{j,j}\neq 0\),\(d_{j,j}\neq 0\)。
通俗地讲,只要我们找到任何一组符合条件的 \(p,q\) 与 \(c,d\),就可以将 \(p,q\) 推广至任意 \(n\) 次多项式,因为 \(\mathbf{C}\) 与 \(\mathbf{D}\) 永远是互逆的,不随 \(p,q\) 的改变而改变。这就意味着为了证明某个反演公式成立,我们只需考虑一种方便证明的特殊情况即可。
2. 二项式反演
2.1. 引入
在玩二项式定理的时候,如果你尝试把 \(x\) 变成 \(1+(x-1)\),那么可能会有新的收获:
耶?和反演公式的形式长得很像诶!那么试试能不能用 \(x\) 表示 \(x-1\):
很好!如果令 \(p_i=x^i\),\(q_i=(x-1)^i\),\(c_{i,j}=\dbinom{i}{j}\),\(d_{i,j}=\dbinom{i}{j}(-1)^{i-j}\),那么我们就成功找到了一组 \(p,q,c,d\)!
综上,我们有:
其中 \(f_n\) 表示至多 \(n\) 个,\(g_n\) 表示恰好 \(n\) 个。这就是二项式反演,上面的引入就是它的证明。
如果将矩阵转置,那么还可以得到另一种表示方法:
其中 \(f_n\) 表示至少 \(n\) 个,\(g_n\) 表示恰好 \(n\) 个。
2.2. 直观理解
实际上二项式反演的本质是容斥原理。\(f_i\) 表示至少 / 至多 \(i\) 个的方案数,而 \(g_i\) 表示恰好 \(i\) 个的方案数。
一般情况下,使用容斥原理时,如果固定 \(i\),那么任选 \(i\) 个元素的贡献不一定相等。这就意味着我们必须 \(2^n\) 枚举每一种情况。而相反,如果任选 \(i\) 个数时贡献只与 \(i\) 有关而与所选择的数无关,由于固定了 \(i\) 故也即贡献相等,就可以简化为二项式反演。这是使用二项式反演的情况之一,另一种情况见应用部分。
2.3. 应用
在 OI 中,二项式反演的应用通常与动态规划相结合(当然也不乏直观解释中的情况,如下方的三类问题):DP 求出至少 / 至多选择 \(i\) 个元素时的总贡献是简单的,那么我们就可以通过二项式反演,得到恰好选择 \(i\) 个元素时的总贡献。第四节例题部分很好地体现了这一点。
综上,我们总结出二项式定理的核心:进行至少 / 至多和恰好的转换。
2.3.1. 错排问题
\(n\) 个人排列,求每个人都恰好站错的方案数。
设 \(g_n\) 表示 \(n\) 个人的错排方案数,而 \(f_n\) 表示所有排列方案数,即 \(n!\),那么显然有 \(f_n=\sum_{i=0}^n\dbinom{n}{i}g_i\),\(i\) 的意义是站错的人的个数。从而反推出 \(g_n=\sum_{i=0}^n(-1)^{n-i}\dbinom{n}{i}f_i=\sum_{i=0}^n(-1)^{n-i}\dfrac{n!}{(n-i)!}\)(组合数分母上的 \(i!\) 与 \(f_i\) 抵消了)。
2.3.2. 染色问题 I(一维)
\(n\) 个格子,\(k\) 种颜色,相邻格子颜色不同,每个颜色至少出现一次,求方案数。
设 \(g_k\) 表示恰好出现 \(k\) 个颜色的方案数,\(f_k\) 表示至多出现 \(k\) 个颜色的方案数,即 \(k\times (k-1)^{n-1}\)。显然有 \(f_k=\sum_{i=0}^k \dbinom{k}{i}g_i\),从而反推出 \(g_k=\sum_{i=0}^k(-1)^{k-i}\dbinom{k}{i}f_i\)。显然可以 \(\mathcal{O}(k\log n)\) 计算。
2.3.3. 染色问题 II(二维)
\(n\times m\) 个格子,\(1\) 种颜色,每行和每列至少有一个格子被涂色,求方案数。
设 \(g_{i,j}\) 表示恰好有 \(i\) 行 \(j\) 列被涂色,\(f_{i,j}\) 表示至多有 \(i\) 行 \(j\) 列被涂色的方案数,即 \(2^{ij}\)。那么对 \(i\) 和 \(j\) 这一维分别二项式反演,有 \(f_{n,m}=\sum_{i=0}^n\dbinom{n}{i}\sum_{j=0}^m \dbinom{m}{j}g_{i,j}\),从而反推出 \(g_{n,m}=\sum_{i=0}^n\sum_{j=0}^m (-1)^{(n-i)+(m-j)}\dbinom{n}{i}\dbinom{m}{j}f_{i,j}\),时间复杂度 \(\mathcal{O}(n^2)\)。
2.3.4. 染色问题 III(三维)
\(n\times m\) 个格子,\(k\) 种颜色,每行和每列至少一个格子被染色,每个颜色至少出现一次,格子可不被涂色,求方案数。
请读者结合染色问题 I. 与 II. 自行思考。
2.4. 参考文章
2.5. 例题
*I. P6478 [NOI Online #2 提高组] 游戏
很不错的题目,可以用来入门二项式反演。
设 \(f_x\) 为钦定 \(x\) 对祖先关系的情况总数(即祖先关系至少有 \(x\) 对),\(g_x\) 为答案,那么根据二项式反演有 \(g_x=\sum_{i=x}^{n}(-1)^{i-x}\dbinom{i}{x}f_i\)。
\(f_x\) 可以树形背包求,别忘了最后乘上 \((m-x)!\),因为剩下 \(m-x\) 对点对顺序可以任意。时间复杂度 \(\mathcal{O}(n^2)\)。
const int N=5e3+5;
ll n,sz[N],a[N],f[N][N],C[N][N];
char s[N];
vector <int> e[N];
void dfs(int id,int fa){
f[id][0]=1;
for(int it:e[id]){
if(it==fa)continue;
dfs(it,id),mem(f[0],0,N);
for(int i=0;i<=sz[id]/2;i++)
for(int j=0;j<=sz[it]/2;j++)
f[0][i+j]=(f[0][i+j]+f[id][i]*f[it][j])%mod;
sz[id]+=sz[it],a[id]+=a[it],cpy(f[id],f[0],N);
}
for(int i=min(a[id],sz[id]-a[id])+1;i;i--)
f[id][i]=(f[id][i]+f[id][i-1]*((s[id]=='1'?a[id]:sz[id]-a[id])-i+1))%mod;
sz[id]++,a[id]+=s[id]=='0';
}
int main(){
scanf("%d%s",&n,s+1);
for(int i=1;i<n;i++){
int u,v; cin>>u>>v;
e[u].pb(v),e[v].pb(u);
} dfs(1,0);
for(int i=0;i<=n/2;i++)
for(int j=2;j<=n/2-i;j++)
f[1][i]=f[1][i]*j%mod;
for(int i=0;i<=n;i++)
for(int j=0;j<=i;j++)
C[i][j]=(j==0||j==i?1:C[i-1][j-1]+C[i-1][j])%mod;
for(int i=0;i<=n/2;i++){
ll ans=0;
for(int j=i;j<=n;j++)
ans=(ans+((j-i)&1?mod-1:1)*C[j][i]%mod*f[1][j])%mod;
cout<<ans<<endl;
}
return 0;
}
*II. P4859 已经没有什么好害怕的了
和上一题极度类似。DP 方法几乎一样,最后也都要乘上阶乘。
实际上,上一题就是把这一题搬到了树上。
ll ksm(ll a,ll b){
ll s=1;
while(b){
if(b&1)s=s*a%mod;
a=a*a%mod,b>>=1;
}
return s;
}
ll inv(ll x){return ksm(x%mod,mod-2);}
ll fc[N],ifc[N];
ll binom(int n,int m){return fc[n]*ifc[m]%mod*ifc[n-m]%mod;}
ll n,k,c,ans;
ll a[N],b[N],f[N];
int main(){
cin>>n>>k,f[0]=1,c=n+k>>1;
if((n&1)^(k&1))puts("0"),exit(0);
for(int i=0;i<=n;i++)fc[i]=i?fc[i-1]*i%mod:1,ifc[i]=inv(fc[i]);
for(int i=1;i<=n;i++)cin>>a[i]; sort(a+1,a+n+1);
for(int i=1;i<=n;i++)cin>>b[i];
for(int i=1;i<=n;i++){
int cnt=0;
for(int j=1;j<=n;j++)cnt+=b[j]<a[i];
for(int j=cnt;j;j--)f[j]=(f[j]+f[j-1]*(cnt-j+1))%mod;
}
for(int i=0;i<=n;i++)f[i]=f[i]*fc[n-i]%mod;
for(int i=c;i<=n;i++)ans=(ans+(i-c&1?mod-1:1)*binom(i,c)%mod*f[i])%mod;
cout<<ans<<endl;
return 0;
}
*III. CF1228E Another Filling the Grid
以前不会做,现在也不会。
设 \(g_{i,j}\) 表示恰好有 \(i\) 行 \(j\) 列的最小值为 \(1\),\(f_{i,j}\) 表示最多有 \(i\) 行 \(j\) 列的最小值为 \(1\),即 \((k-1)^{(n-i)(n-j)}\times k^{n^2-(n-i)(n-j)}\),那么有 \(f_{n,n}=\sum_{i=0}^n\sum_{j=0}^n\dbinom{n}{i}\dbinom{n}{j}g_{i,j}\),二项式反演得到 \(g_{n,n}=\sum_{i=0}^n\sum_{j=0}^n(-1)^{n-i+n-j}\dbinom{n}{i}\dbinom{n}{j}f_{i,j}\)。可以 \(\mathcal{O}(n^2\log k)\) 计算,时间复杂度足够优秀。当然,细节实现可以做到平方甚至线性对数。
l ksm(ll a,ll b){
ll s=1;
while(b){
if(b&1)s=s*a%mod;
a=a*a%mod,b>>=1;
}
return s;
}
ll inv(ll x){return ksm(x%mod,mod-2);}
ll fc[N],ifc[N];
ll binom(int n,int m){return fc[n]*ifc[m]%mod*ifc[n-m]%mod;}
ll n,k,ans;
int main(){
cin>>n>>k;
for(int i=0;i<=n;i++)fc[i]=i?fc[i-1]*i%mod:1,ifc[i]=inv(fc[i]);
for(int i=0;i<=n;i++)
for(int j=0;j<=n;j++){
ll c=(n-i)*(n-j);
ll b=binom(n,i)*binom(n,j)%mod;
ans=(ans+(n-i+n-j&1?mod-1:1)*b%mod*ksm(k,c)%mod*ksm(k-1,n*n-c))%mod;
}
cout<<ans<<endl;
return 0;
}
3. 单位根反演
3.1. 公式
常规证法是用等比数列求和公式,我不喜欢,因为这样不能使初学单位根反演的同学感受到它的优美与自然,从而让他们感觉单位根反演是一拍脑袋想出来的,就很 artificial。
一个比较直观的,我自己的理解就是 \(\omega_n^x\) 在 \(n\mid x\) 时等于 \(1\),\(n\) 个求和后再除以 \(n\) 就是 \(1\);而在 \(n\nmid x\) 时不妨设 \(d=\gcd(x,n)\)。把单位根放到平面直角坐标系里感受一下,\(\sum_{i=0}^{n-1}(\omega_n^x)^i\) 就是把 \(\omega_n^x\) 旋转 \(n\) 次,每次旋转相同的 \(\dfrac{2\pi x}{n}\) 角度。那么最终形成了相同的 \(d\) 组 “向量”,每组内部有 \(\dfrac{n}{d}\) 个在单位圆上均匀分布的 “向量”,根据高中知识可知每一组向量的和都是 \(0\)。
它还有另外一种形式,在做题时经常会遇到:
3.2. 应用
我们为什么要用单位根反演?钻研这个问题,不仅可以帮助我们深入理解单位根反演,在做题时我们也能更容易看出它的蛛丝马迹。
3.2.1. 将 mod 转化为求和
首先看一道经典的单位根反演入门题:给出 \(n,s,a_0,a_1,a_2,a_3\leq 10^{18}\),求 \(\sum_{i=0}^n\dbinom{n}{i}s^ia_{i\bmod 4}\) 对 \(998244353\) 取模。
看到组合数和幂结合,自然想到二项式定理,可是 \(a_{i\bmod 4}\) 的部分断绝了我们的出路 …… 吗?Nope.
如果我们将 \(a_{i\bmod 4}\) 写作 \(\sum_{j=0}^3a_j[i\equiv j\pmod 4]\),再使用单位根反演得到 \(\sum_{j=0}^3a_j\times \dfrac{1}{4}\sum_{k=0}^3\omega_4^{(i-j)k}\),揉进原来的柿子:
交换求和符号并整理,得
逆用二项式定理,得
如果你学过 NTT 就知道在模 \(998244353\) 意义下,原根和单位根可以互换,因此这里的四次单位根就等于 \(g^{\frac{p-1}4}\),一个快速幂解决。时间复杂度 \(\mathcal{O}(\log n)\),有 \(16\) 倍常数。
代码见例题 I.
3.3. 例题
*I. LOJ#6485. LJJ 学二项式定理
ll ksm(ll a,ll b){
ll s=1;
while(b){
if(b&1)s=s*a%mod;
a=a*a%mod,b>>=1;
}
return s;
}
ll T,n,s,a[4],w[4];
int main(){
cin>>T,w[0]=1,w[1]=ksm(3,mod-1>>2),w[2]=w[1]*w[1]%mod,w[3]=w[1]*w[2]%mod;
while(T--){
cin>>n>>s;
for(int i=0;i<4;i++)cin>>a[i];
ll ans=0;
for(int i=0;i<4;i++)
for(int j=0;j<4;j++){
ll base=a[i]*w[(16-i*j)%4]%mod;
ans=(ans+base*ksm((s*w[j])%mod+1,n))%mod;
}
cout<<ans*ksm(4,mod-2)%mod<<endl;
}
return 0;
}
*II. P5591 小猪佩奇学数学
推推柿子:根据 \(\left\lfloor\dfrac{i}k\right\rfloor=\dfrac{i-i\bmod k}k\),得
略去 \(\dfrac 1 k\),括号拆开分别考虑。
-
前半部分:\(\sum_{i=0}^n\dbinom n i p^i\times i\)。
根据 \(\dbinom{n}{m}=\dfrac n m\dbinom {n-1}{m-1}\),得 \(np\sum_{i=0}^n\dbinom {n-1}{i-1} p^ {i-1}\)。
即 \(np(p+1)^{n-1}\)。
-
后半部分:略去负号,得 \(\sum_{i=0}^{n}\dbinom{n}{i}p^i\sum_{j=0}^{k-1}j[i\equiv j\pmod k]\)。
套入单位根反演,得 \(\sum_{i=0}^n\dbinom n ip^i\sum_{j=0}^{k-1}\dfrac j k\sum_{x=0}^{k-1}\omega_k^{(i-j)x}\)。
略去 \(\dfrac 1 k\),交换求和符号,得 \(\sum_{j=0}^{k-1}j\sum_{x=0}^{k-1}\omega_k^{-jx}\sum_{i=0}^n\dbinom ni(p\omega_k^x)^i\)。
套入二项式定理,得 \(\sum_{j=0}^{k-1}j\sum_{x=0}^{k-1}\omega_k^{-jx}(p\omega_k^x+1)^n\)。
然后发现不会了。交换求和顺序,得 \(\sum_{x=0}^{k-1}(p\omega_k^x+1)^n\sum_{j=0}^{k-1}j(\omega_k^x)^{-j}\)。
略去前面的柿子,将 \(\omega_k^{-x}\) 记做 \(c\),仅关注 \(\sum_{j=0}^{k-1}jc^{j}\)。由于 \(k\) 是常数,因此将其记做 \(f(c)\)。
看到这里,你发现了什么?对!就是我们小学二年级就学过的错位相减法!
-
如果 \(c\neq 1\),求 \(cf(c)-f(c)\):比对每一项的系数,有 \(f(c)-cf(c)=\sum_{j=1}^{k-1}c^j-(k-1)c^k\)。
先特判掉 \(k=1\) 的情况,然后用等比数列求和公式得到 \(\sum_{j=1}^{k-1}c^j=-1+\sum_{j=0}^{k-1}c^j=-1+\dfrac{c^k-1}{c-1}\),注意到 \(c^k\) 恒为 \(1\),所以 \(f(c)=\dfrac{-1-(k-1)}{1-c}=\dfrac{k}{c-1}\)。
-
如果 \(c=1\),那么原式等于 \(\sum_{j=0}^{k-1}j=\dfrac{k(k-1)}2\)。
因此直接计算即可!时间复杂度 \(\mathcal{O}(k\log n)\)。
-
ll ksm(ll a,ll b){
ll s=1; a%=mod;
while(b){
if(b&1)s=s*a%mod;
a=a*a%mod,b>>=1;
}
return s;
}
ll inv(ll x){return ksm(x,mod-2);}
ll n,p,k,w,ans,sub;
int main(){
cin>>n>>p>>k,w=ksm(3,(mod-1)/k);
ans=n*p%mod*ksm(p+1,n-1)%mod;
for(int x=0;x<k;x++){
ll om=ksm(w,x),pw=ksm(p*om+1,n),c=inv(om);
if(c==1)sub=(sub+pw*(k*(k-1)/2%mod))%mod;
else sub=(sub+pw*k%mod*inv(mod+c-1))%mod;
}
cout<<(ans+mod-sub*inv(k)%mod)*inv(k)%mod;
return 0;
}
4. 莫比乌斯反演
极其重要的一类反演,对于推式子的帮助很大。
4.1. 前置知识
4.1.1. 数论函数与积性函数
数论函数,就是定义域是正整数的函数。
积性函数,就是对于任意 \(x,y\in \N^{+}\),若 \(\gcd(x,y)=1\),则 \(f(xy)=f(x)f(y)\) 的函数 \(f\)。若完全积性则不需要 \(\gcd(x,y)=1\) 的条件。
一些积性函数举例(接下来的文章中会出现):
- 单位函数 \(\epsilon(n)=[n=1]\)。
- 常数函数 \(1(n)=1\)。
- 恒等函数 \(\mathrm{id}_k(n)=n^k\),\(\mathrm{id}_1(n)\) 通常记作 \(\mathrm {id}(n)\)。
- 除数函数 \(\sigma_k(n)=\sum_{d|n}d^k\)。\(\sigma_0(n)\) 就是约数个数,记作 \(\tau(n)\) 或 \(\mathrm{d}(n)\)。\(\sigma_1(n)\) 就是约数和,记作 \(\sigma(n)\)。
- 大名鼎鼎的欧拉函数 \(\varphi(n)=\sum_{i=1}^n[\gcd(i,n)=1]\)。关于欧拉函数可以看 基础数论学习笔记。
- 本章节的核心莫比乌斯函数 \(\mu(n)=\begin{cases}0&\exist\ d>1,d^2\mid n \\(-1)^{\omega(n)}&\mathrm{otherwise}\end{cases}\),其中 \(\omega(n)\) 表示 \(n\) 本质不同质因子个数。它的积性是显然的。
4.1.2. 狄利克雷卷积(Dirichlet)
由于学习莫比乌斯反演之前需要学会狄利克雷卷积,所以就放在这了。大名鼎鼎的狄利克雷卷积竟沦落到给莫比乌斯反演作铺垫。
定义:设 \(f,g\) 是数论函数,定义它们的狄利克雷卷积为 \(h(x)=\sum_{ab=x}f(a)g(b)\)。显然的,狄利克雷卷积具有交换律,结合律和乘法分配律。它的另一种表达形式为 \(h(x)=\sum_{d|x}f(d)g\left(\dfrac x d\right)\),非常有用。
FMT 和 FWT 叫位运算卷积,FFT 和 NTT 叫加法卷积,不难发现狄利克雷卷积实际上是乘法卷积。
狄利克雷卷积有如下性质:两个积性函数的狄利克雷卷积也是积性函数,积性函数的逆元也是积性函数。证明略。
4.1.3. 莫比乌斯函数
因为莫比乌斯函数是积性函数,所以它可以由线性筛求得。
莫比乌斯函数有以下几条性质:
首先 \(n=1\) 时显然,否则我们令 \(n\) 的本质不同质因数分别为 \(p_1,p_2,\cdots,p_k\),从中选取 \(i\) 个质数的方案数为 \(\dbinom ki\)。由选奇数个方案数之和等于选偶数个方案数之和可知和为 \(0\)。即 \(k>0\) 时 \(\sum_{i=0}^k1^{k-i}\times\dbinom k i\times (-1)^{i}=(1+(-1))^k=0\)(二项式定定理)。
因此我们证明了 \(\mu*1=\epsilon\)。
该式子在推式子时比较有用。可以先证明 \(\varphi*1=\mathrm{id}\),两边同时卷积 \(\mu\) 有 \(\varphi*(1*\mu)=\varphi*\epsilon=\varphi=\mathrm{id}*\mu\)。得证。
这是极其重要的一个柿子,也是莫比乌斯反演的核心。将 \(\gcd(i,j)\) 带入式 \(4.1\) 即得。
线性筛莫比乌斯函数。
mu[1]=1;
for(int i=2;i<N;i++){
if(!vis[i])pr[++cnt]=i,mu[i]=-1;
for(int j=1;j<=cnt&&i*pr[j]<N;j++){
vis[i*pr[j]]=1;
if(i%pr[j]==0)break;
mu[i*pr[j]]=-mu[i];
}
}
4.1.4. 整除分块
哪里有莫反,哪里就有整除分块。
一个经典的例子是求 \(\sum_{i=1}^n\left\lfloor\dfrac n i\right\rfloor\),\(n\leq 10^{12}\)。注意到不同的 \(\left\lfloor\dfrac n i\right\rfloor\) 的个数实际上是 \(\sqrt n\) 级别的,故使用整除分块:对于一个 \(i\),我们要找到最大的 \(j\) 使得 \(\left\lfloor\dfrac n i\right\rfloor=\left\lfloor\dfrac n j\right\rfloor\),可以证明 \(j=\left\lfloor\dfrac n {\left\lfloor\dfrac n i\right\rfloor}\right\rfloor\)。其实很好理解:找到使 \(\left\lfloor\dfrac n j\right\rfloor=c\) 的最大的 \(j\),只需要用 \(n\) 除以 \(c\) 下取整。
那么,\(l\) 从 \(1\) 开始,每次令 \(r\gets \min\left(n,\left\lfloor\dfrac n {\left\lfloor\dfrac n l\right\rfloor}\right\rfloor\right)\),然后将答案加上 \((r-l+1)\times \left\lfloor\dfrac n l\right\rfloor\) 即可。最后 \(l\gets r+1\),若大于 \(n\) 则退出。
注意当被除数 \(k\) 和枚举上界 \(n\) 不同且 \(k<n\) 时,需要特判 \(\left\lfloor\dfrac k l \right\rfloor=0\)。
4.2. 公式
若 \(f,g\) 是两个数论函数,那么有
证明:已知 \(g*1=f\),则 \(g*1*\mu=g=f*\mu\)。
证明(来自 OI - Wiki):
4.3. 直观理解
只会套皮是不行的,要想熟练运用,不仅要多刷题,也要理解莫反的本质:容斥原理。其中莫比乌斯函数就是容斥系数。也许你会问:容斥系数不应该是 \((-1)^k\) 吗?其实在某种程度上没错,毕竟对比 \((-1)^k\) 与莫比乌斯函数的定义,它们的形式非常相近:只有当 \(x\) 是完全平方数的倍数时容斥系数才为 \(0\)(这其实也暗示了这样的 \(x\) 不同于别的数)。但我们是对于全体自然数做容斥,会有一些奇怪的事情发生:一个自然数被另一个自然数完全包含(这也说明它一定是完全平方数的倍数,接下来会解释),而在容斥原理中,一个集合被另一个集合包含这样的事情是不会发生的。
不妨设接下来有 \(f_n=\sum_{d\mid n}g_d\)。
4.3.1. \(n=2\)
当 \(n=2\) 时,为了求出 \(g_2\),我们只需要用 \(f_2-f_1=(g_2+g_1)-g_1\)。这里已经可以看见容斥的影子了。不妨结合下图以更好理解。
4.3.2. \(n=6\) 及 \(\prod p_i\)
当 \(n=6\) 时,为了求出 \(g_6\),我们首先需要 \(f_6\),但是这样会多加 \(g_1+g_2+g_3\),所以减去 \(f_2+f_3\),但是这样又多减了 \(g_1\),所以我们还应加上 \(f_1\)。结合下图以更好理解。
如果 \(n\) 是若干个不同质数 \(p_1,p_2,\cdots,p_k\) 的乘积,那么画出能够表示 \(n\) 的所有约数的韦恩图(\(k>3\) 时画不出来,不过可以理解一下)。\(k\) 个集合相交形成的所有子集就是 \(n\) 的所有约数。比如说,如果有一块区域落在集合 \(p_1\) 和集合 \(p_3\) 中,那么它所表示的约数就是 \(p_1p_3\)。显然,如果 \(x\mid y\),那么区域 \(y\) 包含于区域 \(x\)。
图源 https://blog.csdn.net/Summer__show_/article/details/76269088 。
不妨将 \(f_d\) 看做整块区域 \(d\)(比如说上图中区域 \(30\) 也属于区域 \(6\)),而 \(g_d\) 看做仅属于区域 \(d\) 的部分,然后将所有区域表示的数 \(i\) 替换成 \(\dfrac n i\),那么 \(f_n\) 就表示最外层的大区域,即所有 \(g_d\) 的和,而 \(f_1\) 就表示最里层的小区域,即 \(g_1\),刚好符合 \(f_n=\sum_{d\mid n} g_d\) 的条件。因此,由于 \(f\) 已知,为了得到仅属于区域 \(n\) 的部分所表示的数 \(g_n\),我们只需要进行容斥:加上 \(f_n\),减去 \(f_{p_1},\cdots, f_{p_k}\) …… 不难发现每个数前的容斥系数就是 \(-1\) 的 \(\dfrac n i\) 所含质因子个数次幂,即 \(\mu\left(\dfrac n i\right)\)(\(n\) 由互不相同的质因子相乘得到,那么 \(\dfrac n i\) 也如此)。这不就是 \(g_n=\sum_{d\mid n}f_d\mu\left(\dfrac n d\right)\) 吗!
4.3.3. \(n=4\) 及任意自然数
有了上面的经验,你应该很快知道表示约数 \(4\) 的区域被表示约数 \(2\) 的区域 “完全包含”,这里的 \(y\) 被 \(x\) “完全包含” 其实指的是:\(y\) 包含于的所有集合等于 \(x\) 包含于的所有集合且 \(y\mid x\)。画出韦恩图就是一大一小两个圆,其中一个圆完全落在另一个里面。因此,如果计算了 \(2\) 的贡献,那么完全没有必要再算 \(4\) 了,因为 \(4\) 被 \(2\) “完全包含”,所以它们的 “多加/多减状态” 应该是相同的。也就是说算完 \(2\) 的贡献,\(4\) 的贡献也恰好算完,既不多加,也不多减,因而 \(\mu(4)=0\)。不难发现,这种情况仅在一个数是非 \(1\) 的完全平方数的倍数时出现,从而推广至任意自然数。
综上,莫比乌斯函数可以看作对 \(\N^{+}\) 做容斥时的容斥系数。
4.4. 应用
一般用来化简带有 \(\gcd\) 的柿子,几乎所有莫反题都需要用到整除分块。
具体应用可以看 OI-Wiki,抄在博客里其实意义不大。
4.5. 例题
下面所有分式都是下取整。
I. P2522 [HAOI2011]Problem b
二维差分将下界化为 \(1\),然后推式子
将 \(k\) 搞掉:
莫反:
枚举约数 \(d\),记 \(c=\min\left(\dfrac n k,\dfrac m k\right)\):
由于 \(1\sim x\) 中 \(y\) 的倍数有 \(\dfrac x y\) 个,故原式化为:
整除分块即可,时间复杂度 \(\mathcal{O}(n+T\sqrt n)\)。
long long 改成 int,11s 变成 5s,离谱。
const int N=5e4+5;
int cnt,pr[N],vis[N],mu[N];
int a,b,c,d,k;
int calc(int x,int y){
x/=k,y/=k;
int c=min(x,y),ans=0;
for(int l=1,r;l<=c;l=r+1){
r=min(x/(x/l),y/(y/l));
ans+=(x/l)*(y/l)*(mu[r]-mu[l-1]);
} return ans;
}
void solve(){
a=read(),b=read(),c=read(),d=read(),k=read();
cout<<calc(b,d)-calc(b,c-1)-calc(a-1,d)+calc(a-1,c-1)<<"\n";
}
int main(){
mu[1]=1;
for(int i=2;i<N;i++){
if(!vis[i])pr[++cnt]=i,mu[i]=-1;
for(int j=1;j<=cnt&&i*pr[j]<N;j++){
vis[i*pr[j]]=1;
if(i%pr[j]==0)break;
mu[i*pr[j]]=-mu[i];
} mu[i]+=mu[i-1];
}
int T=read();
while(T--)solve();
return 0;
}
II. SP5971 LCMSUM - LCM Sum
来推式子。
*III. P4318 完全平方数
莫比乌斯函数作为容斥系数的经典例子。
显然答案满足可二分性,于是考虑如何求 \(x\) 以内不含有平方因子的数的个数。仍然是容斥:我们只需要加上所有由偶数个质数相乘的平方的倍数,减去由奇数个质数相乘的平方的倍数。发现这就是 \(\sum_{i^2\leq n}\mu(i)\dfrac{n}{i^2}\),直接暴力求就行了。时间复杂度 \(\mathcal{O}(T\log k\sqrt k)\)。
const int N=1e5+5;
int T,n,cnt,pr[N],vis[N],mu[N];
ll calc(ll x){
ll res=0;
for(int i=1;i*i<=x;i++)res+=mu[i]*(x/i/i);
return res;
}
int main(){
cin>>T,mu[1]=1;
for(int i=2;i<N;i++){
if(!vis[i])pr[++cnt]=i,mu[i]=-1;
for(int j=1;j<=cnt&&i*pr[j]<N;j++){
vis[i*pr[j]]=1;
if(i%pr[j]==0){
mu[i*pr[j]]=0;
break;
}
mu[i*pr[j]]=-mu[i];
}
}
while(T--){
cin>>n; ll l=n,r=n<<1;
while(l<r){
ll m=l+r>>1;
if(calc(m)<n)l=m+1;
else r=m;
} cout<<l<<endl;
}
return 0;
}
IV. [BZOJ2986]Non-Squarefree Numbers
双倍经验 P4318 完全平方数。代码从 \(\omega(i)=1\) 时开始容斥,此时容斥系数为 \(1\)(从 \(\omega (i)=0\) 即 \(i=1\) 开始容斥也可以),故筛出的莫比乌斯函数是实际定义的相反数。
template <class T> void cmin(T &a, T b){a = a < b ? a : b;}
template <class T> void cmax(T &a, T b){a = a > b ? a : b;}
bool Mbe;
const int N = 2e5 + 5;
int mu[N], pr[N], vis[N], cnt;
ll n, l, r;
void sieve() {
for(int i = 2; i < N; i++) {
if(!vis[i]) pr[++cnt] = i, mu[i] = 1;
for(int j = 1; j <= cnt && i * pr[j] < N; j++) {
vis[i * pr[j]] = 1;
if(i % pr[j] == 0) break;
mu[i * pr[j]] = mu[i] * -1;
}
}
}
ll check(ll lim) {
ll res = 0;
for(ll i = 2; i * i <= lim; i++) res += lim / i / i * mu[i];
return res;
}
bool Med;
int main(){
fprintf(stderr, "%.2lf\n", (&Mbe - &Med) / 1048576.0);
sieve(), cin >> n, l = 1, r = n << 2;
while(l < r) {
ll m = l + r >> 1;
if(check(m) < n) l = m + 1;
else r = m;
}
cout << l << endl;
return 0;
}