• 容斥原理学习笔记


    引入

    用一个简单的例子来引入容斥:

    这个例子可以说是非常经典,一共有三种活动 {1,2,3}{1,2,3}
    同时你可以查询 f(S)f(S) 会返回参加了 SS 集合中活动的人的个数。
    现在的问题是求至少有多少个人参加了活动。

    我记得这个是班上高一上集合时的作业最后一题。
    看上去非常奇怪,但是可以发现如果根据题目意思画一个韦恩图就是非常简单的事了。
    这里省略画图的过程,答案还是比较显然的:

    f({1})+f({2})+f({3})f({1,2})f({1,3})f({2,3})+f({1,2,3})
    f({1})+f({2})+f({3})f({1,2})f({1,3})f({2,3})+f({1,2,3})

    前置知识

    二项式定理

    可以发现对于 (a+b)n(a+b)n 的各个位的系数和杨辉三角有很大的关系。
    杨辉三角满足 Ti,j=Ti1,j+Ti1,j1Ti,j=Ti1,j+Ti1,j1
    我们记 (nm)(nm) 表示组合数形如 CmnCmn
    容易得到:

    (nm)=Cmn=n!m!(nm)!
    (nm)=Cmn=n!m!(nm)!

    直接给出二项式定理的结论,证明的话直接归纳。

    (a+b)n=ni=0(ni)aibni
    (a+b)n=i=0n(ni)aibni

    组合数的相关公式

    其实上过小学的人都知道,组合数和杨辉三角也是对应的。

    (ni)=(n1i)+(n1i1)
    (ni)=(n1i)+(n1i1)

    如果要证明的话可以直接化简一下式子。

    (ni)=n!i!(ni)!(n1i)=(n1)!i!(n1i)!=n!(ni)i!n(ni)!(n1i1)=(n1)!(i1)!(ni)!=n!ii!n(ni)!
    (ni)(n1i)(n1i1)=n!i!(ni)!=(n1)!i!(n1i)!=n!(ni)i!n(ni)!=(n1)!(i1)!(ni)!=n!ii!n(ni)!

    式子化简到这一步也就是非常显然的了。

    奇奇怪怪的知识(可能用不到)

    在莫比乌斯反演中好像比较吃香,就是求和符号可以随便换位置。
    具体就不举例子了。

    正文

    最简单的容斥还是之前引入的那个问题。
    我们仍然记 f(S)f(S) 为参加了 SS 这个集合中所有活动的人的个数。
    但是我们这次要从公式化的角度去得到答案,我们记 UU 表示所有的元素,则:

    Ans=SUf(S)(1)|s|+1
    Ans=SUf(S)(1)|s|+1

    还是非常简单的,小学生画一个韦恩图都可以搞明白。

    比较牛逼的容斥方法

    首先我们令之前的 f(s)f(s) 查询操作改名为 q(s)q(s)
    g(x)g(x) 表示一个在结合 xx 中的元素提供的贡献,f(x)f(x) 表示容斥系数。
    他们的关系可以这样表示:

    g(n)=ni=1(ni)f(i)
    g(n)=i=1n(ni)f(i)

    接下来我们可以发现,f(x)f(x) 可以通过递推得到。

    g(n+1)=n+1i=1(n+1i)f(i)
    g(n+1)=i=1n+1(n+1i)f(i)

    因为当 i=n+1i=n+1(n+1i)=(n+1n+1)=1(n+1i)=(n+1n+1)=1 ,所以可以直接提取出 f(n+1)f(n+1)

    g(n+1)=ni=1(n+1i)f(i)+f(n+1)f(n+1)=g(n+1)ni=1(n+1i)f(i)
    g(n+1)=i=1n(n+1i)f(i)+f(n+1)f(n+1)=g(n+1)i=1n(n+1i)f(i)

    直接搞就可以啦。
    听说还有多项式求逆的 O(nlogn)O(nlogn) 的做法,但是我不会,先咕一下。

    一些应用或者入门题

    1. 还是之前引入时的题目,可以进行一些转化。

    直接套用公式就可以了,这里就再把式子放一下,不再进行解释:

    Ans=SUf(S)(1)|s|+1
    Ans=SUf(S)(1)|s|+1

    2.错排问题

    错排就是对于 1n1n 的排列 aiai ,对于所有的 ii ,满足 aiiaii
    直接算贡献不太好算,计算出不合法也就是存在 ai=iai=i 的情况。
    我们假设有 numnum 个位置满足 ai=iai=i ,那么剩下来的怎么拍都可以,也就是 (nnum)!(nnum)!
    我们用 f(n)f(n) 表示长度为 nn 的排列的错排个数。
    很容易列出下面的这个式子:

    f(n)=ni=0(1)i(ni)(ni)!
    f(n)=i=0n(1)i(ni)(ni)!

    这个式子是非常显然的,现在来看看可不可以搞出一个递推式。

    f(n+1)=n+1i=0(1)i(n+1i)(n+1i)!=ni=0(1)i(n+1i)(n+1i)!+(1)n+1(n+1n+1)(n+1n1)!=(1)n+1+ni=0(1)i(n+1i)(n+1i)!
    f(n+1)=i=0n+1(1)i(n+1i)(n+1i)!=i=0n(1)i(n+1i)(n+1i)!+(1)n+1(n+1n+1)(n+1n1)!=(1)n+1+i=0n(1)i(n+1i)(n+1i)!

    前面非常清楚了,现在来看后面的那一个部分:

    Num=ni=0(1)i(n+1i)(n+1i)!=ni=0(1)i(ni)×(n+1)×(ni)!×(n+1)=(n+1)2ni=0(1)i(ni)(ni)!
    Num=i=0n(1)i(n+1i)(n+1i)!=i=0n(1)i(ni)×(n+1)×(ni)!×(n+1)=(n+1)2i=0n(1)i(ni)(ni)!

    可以发现现在和之前的式子

    f(n)=ni=0(1)i(ni)(ni)!
    f(n)=i=0n(1)i(ni)(ni)!

    挂上了关系。
    所以就可以得到递推式:

    f(n)=(1)n+nf(n1)
    f(n)=(1)n+nf(n1)

    3.Hdu 上的一道简单题 Co-prime

    题目描述:给你 llrrnn ,问 [l,r][l,r] 中有多少个数和 nn 互质。
    其中 l,r1015l,r1015n109n109

    很容易发现,直接计算不太好算,所以采用类似前缀和的方法。
    我们记录 f(i)f(i) 表示 [1,i][1,i] 中和 nn 互质数的个数,那么答案是 f(r)f(l1)f(r)f(l1)
    现在来考虑怎么计算出 f(i)
    发现最坏的情况下 n 只会有 10 个质因数组成,所以我们先对 n 质因数分解。
    然后直接 pnum! 暴力枚举每一种可能性就可以了。
    直接容斥,具体细节直接看代码:

    复制代码
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    cpp
    #include <set> #include <map> #include <cmath> #include <queue> #include <string> #include <cstdio> #include <cctype> #include <vector> #include <cstring> #include <iostream> #include <algorithm> #include <unordered_map> #define File(a) freopen(a".in", "r", stdin), freopen(a".out", "w", stdout) #define quad putchar(' ') #define Enter putchar('\n') using std::abs; using std::pair; using std::string; using std::make_pair; #define int long long template <class T> void read(T &a); template <class T> void write(T x); template <class T, class ...rest> void read(T &a, rest &...x); template <class T, class ...rest> void write(T x, rest ...a); int T, l, r, n, num[50], tot, ok[50], ans1, ans2; inline void get_prime(int n) { for (int i = 2; i * i <= n; i++) { if (n % i == 0) { num[++tot] = i; while (n % i == 0) n /= i; } } if (n != 1) num[++tot] = n; return ; } inline void dfs(int k) { if (k > tot) { int chose = 0, tmp = -1, mul = 1; for (int i = 1; i <= tot; i++) { chose += ok[i]; mul = mul * (ok[i] == 1 ? num[i] : 1); } if (chose % 2 == 0) tmp = 1; ans1 += (l - 1) / mul * tmp; ans2 += r / mul * tmp; return ; } ok[k] = 1; dfs(k + 1); ok[k] = 0; dfs(k + 1); } signed main(void) { read(T); for (int test = 1; test <= T; test ++) { tot = ans1 = ans2 = 0; memset(num, 0, sizeof(num)); read(l, r, n); get_prime(n); dfs(1); printf("Case #%d: ", test); write(ans2 - ans1); Enter; } return 0; } /* 2 1 10 2 3 15 5 */ template <class T> void read(T &a) { int s = 0, t = 1; char c = getchar(); while (!isdigit(c) && c != '-') c = getchar(); if (c == '-') c = getchar(), t = -1; while (isdigit(c)) s = s * 10 + c - '0', c = getchar(); a = s * t; } template <class T> void write(T x) { if (x == 0) putchar('0'); if (x < 0) putchar('-'), x = -x; int top = 0, sta[50] = {0}; while (x) sta[++top] = x % 10, x /= 10; while (top) putchar(sta[top] + '0'), top --; return ; } template <class T, class ...rest> void read(T &a, rest &...x) { read(a); read(x...); } template <class T, class ...rest> void write(T x, rest ...a) { write(x); quad; write(a...); }
    折叠
  • 相关阅读:
    商用车市场「跌跌不休」,主动安全「让位」智能驾驶?
    Python之数据库(MYSQL)连接
    Deep-RNN-深度循环神经网络(RNN循环神经网络)
    “AURORA-M:首个遵循人类审查安全指令微调的开源多语言模型
    www.7seasnft.com、数字藏品、总结
    Tomcat 源码分析 (连接器) (六)
    越复杂的CoT越有效吗?Complexity-Based Prompting for Multi-step Reasoning
    Java使用多线程做批处理(查询大量数据)
    Tomcat的安装和使用
    安装Canal1.1.5 并监控mysql8的binlog
  • 原文地址:https://www.cnblogs.com/Oier-GGG/p/16472395.html