c莫比乌斯函数_数论——容斥原理、莫比乌斯函数

数论——容斥原理、莫比乌斯函数由于某些玄学原因,有些公式的减号未显示,如果有的符号显示为空格,则默认为减号。

1、容斥原理:时间复杂度为

equation?tex=O%282%5EN%29,下面会有证明。

举一个简单的例子:用韦恩图来思考,求

equation?tex=S1

equation?tex=S2

equation?tex=S3三个集合的原有元素的并集,那么结果为:

equation?tex=S1%2BS2%2BS3-S1+%5Ccap+S2-S1+%5Ccap+S3-S2+%5Ccap+S3%2BS1+%5Ccap+S2+%5Ccap+S3

以此类推到

equation?tex=N个圆的交集:用容斥原理的方法答案为所有单个集合的元素个数-所有两个集合互相交集的元素个数+所有三个集合互相交集的元素个数...

我们知道容斥原理公式一共涉及到的元素个数为:

equation?tex=C_N%5E1%2BC_N%5E2%2BC_N%5E3%2B...%2BC_N%5EN。因为

equation?tex=C_N%5E0%2BC_N%5E1%2BC_N%5E2%2BC_N%5E3%2B...%2BC_N%5EN%3D2%5En,因此

equation?tex=C_N%5E1%2BC_N%5E2%2BC_N%5E3%2B...%2BC_N%5EN%3D2%5En-1,因此容斥原理公式一共涉及到的元素个数为

equation?tex=2%5En%E2%80%941。关于此公式(

equation?tex=C_N%5E0%2BC_N%5E1%2BC_N%5E2%2BC_N%5E3%2B...%2BC_N%5EN%3D2%5En)的证明,我们可以假设等号左边为对于

equation?tex=N个物品所有选法的总个数,等号右边考虑每个物品选与不选两种情况,因此等式成立。

因此容斥原理的时间复杂度为

equation?tex=O%282%5EN%29

容斥原理的证明:对于容斥原理

equation?tex=%7CS1+%5Ccup+S2+%5Ccup+...+%5Ccup+SN%7C%3D%5Csum_%7Bi%3D1%7D%5EN%7BSi%7D-%5Csum_%7Bi%2C+j%7D%5EN%7BSi+%5Ccap+Sj%7D%2B%5Csum_%7Bi%2C+j%2C+k%7D%5EN%7BSi+%5Ccap+Sj+%5Ccap+Sk%7D%2B... 对于一个元素

equation?tex=x,它在

equation?tex=k个集合中,

equation?tex=1+%5Cleq+k+%5Cleq+N,它本身被选择的次数为

equation?tex=C_k%5E1%E2%80%94C_k%5E2%2BC_k%5E3-...%2B%28%E2%80%941%29%5E%7Bk-1%7DC_k%5Ek。我们知道一个结论:

equation?tex=C_k%5E1-C_k%5E2%2BC_k%5E3-...%2B%28-1%29%5E%7Bk-1%7DC_k%5Ek%3D1,因此对于每一个元素

equation?tex=x,它只被计算了

equation?tex=1次,证毕。

例题:AcWing 890. 能被整除的数

给定一个整数

equation?tex=n

equation?tex=m个不同的质数

equation?tex=p1%2Cp2%2C%E2%80%A6%2Cpm。请你求出

equation?tex=1

equation?tex=n中能被

equation?tex=p1%2Cp2%2C%E2%80%A6%2Cpm中的至少一个数整除的整数有多少个。

首先我们知道,在

equation?tex=N个数中能被

equation?tex=x整除的数的个数为

equation?tex=%5Clfloor%7BN%2Fx%7D%5Crfloor

因此我们只需要根据容斥原理,求出可以被单个元素整除的个数之和-可以被两个元素整除的个数之和+可以被三个元素整除的个数之和...我们用位运算来求得答案,时间复杂度为

equation?tex=O%282%5EN%29

#include

#define int long long

using namespace std;

int p[20];

void work() {

int n, m;

cin >> n >> m;

for (int i = 0; i < m; i++) cin >> p[i];

int res = 0;

for (int i = 1; i < 1 << m; i++) {

int t = 1, s = 0;

for (int j = 0; j < m; j++)

if (i >> j & 1) {

if (t * p[j] > n) {

t = -1;

break;

}

t *= p[j];

s++;

}

if (t != -1) {

if (s % 2) res += n / t;

else res -= n / t;

}

}

cout << res << endl;

}

int32_t main() {

ios::sync_with_stdio(false);

cin.tie(0);

cout.tie(0);

int cas;

cas = 1;

while (cas--) work();

}

例题:AcWing 214. Devu和鲜花

equation?tex=N个盒子,第

equation?tex=i个盒子中有

equation?tex=Ai枝花。同一个盒子内的花颜色相同,不同盒子内的花颜色不同。要从这些盒子中选出

equation?tex=M枝花组成一束,求共有多少种方案。若两束花每种颜色的花的数量都相同,则认为这两束花是相同的方案。结果需对

equation?tex=10%5E9%2B7取模之后方可输出。

我们先考虑:从

equation?tex=N个盒子选

equation?tex=M枝花,每个盒子的花的个数为无限个,问一共能选多少枝花?

此问题等价于从

equation?tex=N个盒子选

equation?tex=M%2BN%E6%9E%9D花,那么每个盒子至少选

equation?tex=1枝。那么此问题由等价于把

equation?tex=N%2BM个点分成

equation?tex=N份,我们可以用隔板法来做,一共有

equation?tex=N%2BM-1个空隙,有

equation?tex=N-1个板子,因此答案为

equation?tex=C_%7BN%2BM-1%7D%5E%7BN-1%7D

拓展到此问题,第

equation?tex=i个盒子中有

equation?tex=Ai枝花。那么我们可以反过来考虑,用总共的答案

equation?tex=C_%7BN%2BM-1%7D%5E%7BN-1%7D减去其中第

equation?tex=i个盒子被拿走了大于

equation?tex=Ai枝花的方案。第

equation?tex=i个盒子被拿走了大于

equation?tex=Ai枝花的方案数为:假设此盒子已经被拿走了

equation?tex=Ai%2B1枝花,那么等价于前面的问题,从

equation?tex=N个盒子中共拿走

equation?tex=M-Ai-1枝花的方案数,等价于从N个盒子拿走

equation?tex=M-Ai-1%2BN的方案数,每个盒子至少被拿

equation?tex=1枝。因此答案为

equation?tex=C_%7BM%E2%80%94Ai%E2%80%941%2BN%E2%80%941%7D%5E%7BN-1%7D

根据容斥原理来做,可知答案为总共的

equation?tex=C_%7BN%2BM-1%7D%5E%7BN-1%7D减去所有

equation?tex=1个盒子不满足的加上所有

equation?tex=2个盒子不满足的减去所有

equation?tex=3个盒子不满足的...

#include

#define int long long

using namespace std;

int A[20];

constexpr int mod = 1e9 + 7;

int down = 1;

int qmi(int a, int b, int p) {

int res = 1;

while (b) {

if (b & 1) res = res * a % p;

a = a * a % p;

b >>= 1;

}

return res;

}

int C(int a, int b) {

if (a < b) return 0;

int up = 1;

for (int i = a; i > a - b; i--) up = i % mod * up % mod;

return up * down % mod;

}

void work() {

int n, m;

cin >> n >> m;

for (int i = 0; i < n; i++) cin >> A[i];

for (int j = 1; j <= n - 1; j++) down = j * down % mod;

down = qmi(down, mod - 2, mod);

int res = 0;

for (int i = 0; i < 1 << n; i++) {

int a = n + m - 1, b = n - 1;

int sign = 1;

for (int j = 0; j < n; j++)

if (i >> j & 1) {

sign *= -1;

a -= A[j] + 1;

}

res = (res + C(a, b) * sign) % mod;

}

cout << (res % mod + mod) % mod << endl;

}

int32_t main() {

ios::sync_with_stdio(false);

cin.tie(0);

cout.tie(0);

int cas;

cas = 1;

while (cas--) work();

}

2、莫比乌斯函数:

我们举例一道经典的应用题,求

equation?tex=1

equation?tex=N中与

equation?tex=a互质的数的个数:那么根据容斥原理,设

equation?tex=S_i

equation?tex=1

equation?tex=N中和

equation?tex=a有公因子i的数的个数,答案为

equation?tex=N-S_2-S_3-S_5-S_7..%2BS_%7B2%2C3%7D%2BS_%7B3%2C5%7D%2BS_%7B2%2C5%7D%2B...,我们可以惊奇的发现,其答案为

equation?tex=%5Csum_%7Bi%3D0%7D%5E%7BN%7D%7Bu%28i%29%2AS_i%7D

我们可以根据线性筛质数在

equation?tex=O%28N%29的时间内算出前

equation?tex=N个数的莫比乌斯数。

int primes[N], cnt;

bool st[N];

int mobius[N];

void init(int n) {

mobius[1] = 1;

for (int i = 2; i <= n; i++) {

if (!st[i]) {

primes[cnt++] = i;

mobius[i] = -1;

}

for (int j = 0; primes[j] * i <= n; j++) {

int t = primes[j] * i;

st[t] = true;

if (i % primes[j] == 0) {

mobius[t] = 0;

break;

}

mobius[t] = mobius[i] * -1;

}

}

}

AcWing 215. 破译密码

对于给定的整数

equation?tex=a

equation?tex=b

equation?tex=d,有多少正整数对

equation?tex=x

equation?tex=y,满足

equation?tex=x+%5Cleq+a

equation?tex=y%5Cleq+b,并且

equation?tex=gcd%28x%2C+y%29%3Dd

equation?tex=5e4组询问,

equation?tex=a%E3%80%81b的数据范围为

equation?tex=5e4

根据数据的范围,我们可以推断出时间复杂度为

equation?tex=O%28n%5Csqrt+n%29

每次询问的问题等价于:有多少正整数对

equation?tex=x

equation?tex=y,满足

equation?tex=x+%5Cleq+a%2Fd

equation?tex=y%5Cleq+b%2Fd,并且

equation?tex=gcd%28x%2C+y%29%3D1。那么根据容斥原理:值为

equation?tex=min%28a%2Fd%2C+b%2Fd%29-S_2-S_3-S_5%2BS_%7B2%2C3%7D%2BS_%7B2%2C5%7D%2BS_%7B3%2C5%7D-S%7B2%2C3%2C5%7D...,其答案为

equation?tex=%5Csum_%7Bi%3D0%7D%5E%7Bmin%28a%2Fd%2C+b%2Fd%29%7D%7Bu%28i%29%2AS_i%7D,也就是

equation?tex=%5Csum%5E%7Bmin%28a%2Cb%29%7D_i%3Da%2Fi%E2%88%97b%2Fi%E2%88%97u%5Bi%5D。我们可以推断出时间复杂度为

equation?tex=O%28n%29

考虑把上述过程优化,发现,这个式子中虽然i要枚举

equation?tex=N次,但是实际上因为整除的原因

equation?tex=ai的值很少,只有

equation?tex=2%5Csqrt+a个。

因为

equation?tex=a%2F1+%E3%80%81a%2F2%E3%80%81a%2F3%E3%80%81%E2%80%A6是单调递减的,并且有的值相同,所以整个序列一共有有

equation?tex=2%5Csqrt+a个值。(证明:在分母为

equation?tex=1

equation?tex=%5Csqrt+a之间,值的个数为

equation?tex=a+%2F+%5Csqrt+a个值,在

equation?tex=%5Csqrt+a+%2B1

equation?tex=n之间,值的个数为

equation?tex=a+%2F+%5Csqrt+a个值)。

equation?tex=g%28x%29表示

equation?tex=a%2Fx的取值不变的最大的

equation?tex=x值,那么

equation?tex=a%2Fx%3Da%2Fg%28x%29,并且

equation?tex=a%2Fx%3Ea%2F%28g%28x%29%2B1%29,其中

equation?tex=g%28x%29%3Da%2F%28a%2Fx%29

证明

equation?tex=a%2Fx%3Da%2Fg%28x%29

证明:

equation?tex=g%28x%29%3Da%2F%28a%2Fx%29

综上:将原来的序列分成

equation?tex=2%5Csqrt+a段,而且每次都会跳一段,所以总共会跳

equation?tex=2%5Csqrt+a次,时间复杂度就是

equation?tex=O%28%5Csqrt+a%29

加上询问后总的时间复杂度为

equation?tex=O%28n%5Csqrt+n%29

#include

typedef long long ll;

using namespace std;

const int N = 50010;

int primes[N], cnt;

bool st[N];

int mobius[N], sum[N];

void init(int n) {

mobius[1] = 1;

for (int i = 2; i <= n; i++) {

if (!st[i]) {

primes[cnt++] = i;

mobius[i] = -1;

}

for (int j = 0; primes[j] * i <= n; j++) {

int t = primes[j] * i;

st[t] = true;

if (i % primes[j] == 0) {

mobius[t] = 0;

break;

}

mobius[t] = mobius[i] * -1;

}

}

for (int i = 1; i <= n; i++) sum[i] = sum[i - 1] + mobius[i];

}

void work() {

int a, b, d;

cin >> a >> b >> d;

a /= d, b /= d;

int n = min(a, b);

ll res = 0;

for (int l = 1, r; l <= n; l = r + 1) {

r = min(n, min(a / (a / l), b / (b / l)));

res += (sum[r] - sum[l - 1]) * (ll)(a / l) * (b / l);

}

cout << res << endl;

}

int32_t main() {

ios::sync_with_stdio(false);

cin.tie(0);

cout.tie(0);

init(N - 1);

int cas;

cin >> cas;

while (cas--) work();

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值