容斥学习笔记

容斥原理

原理

\[|\bigcup_{i=1}^nA_i|=\sum_{j=1}^n(-1)^{j-1}\sum_{a_k\not=a_ {k+1}}\bigcap_{l=1}^mA_{a_i}\]

这东西学过小学奥数就会了。

一些有用的结论:

\[|\bigcap_{i=1}^nA_i|=|\Omega| - |\bigcup_{i=1}^n\overline{A_i}| \]

应用

基础应用

  • 错排

问题: 求出大小为 \(n\) 的排列的错排数。

思路:

很明显可以用 \(D_n=(n-1)(D_{n-1}+D_{n-2})\) 来做,但是其实也可以容斥。

\(S_i\)\(P_i\not= i\) 的排列集合。

答案即为:\(|\bigcap_{i=1}^nS_i|\)

根据结论:

\[\begin{aligned} |\bigcap_{i=1}^nS_i| &=|\Omega|-|\bigcup_{i=1}^n\overline{S_i}|\\ &=|\Omega|-\sum_{m=1}^n(-1)^{m-1}\sum_{a_1 \sim a_m}|\bigcap_{i=1}^m\overline{S_{a_i}}| \end{aligned} \]

\(S\) 的补集就是 \(P_i = i\) 的排列,相当于有 \(m\) 个位置确定了,所以:

\[\begin{aligned} |\Omega|-\sum_{m=1}^n(-1)^{m-1}\sum_{a_1 \sim a_m}|\bigcap_{i=1}^n\overline{S_{a_i}}| &=|\Omega|-\sum_{m=1}^n(-1)^{m-1}\binom{n}{m}(n-m)!\\ &=|\Omega|-\sum_{m=1}^n(-1)^{m-1}\frac{n!}{m!}\\ &=n!-n!\sum_{m=1}^n(-1)^{m-1}\frac{1}{m!}\\ &=n!\sum_{m=0}^n\frac{(-1)^m}{m!}\\ \end{aligned} \]

这样就得到了 \(D_n\) 的通项公式,可以证明这个数趋近于 \(\frac{1}{e}\)

  • 第二类斯特林数

问题: 求第二类斯特林数 \(n,k\),即 \(n\) 个球放入 \(k\) 个盒子,盒子无顺序,盒子非空。

思路:

同样,也有递推公式,但是也可以容斥。

首先,假定盒子有顺序,最后除以 \(k!\) 即可。

\(S_i\):第 \(i\) 个盒子非空的方案。

答案即为:\(|\bigcap_{i=1}^nS_i|\)

同理,转化成求:

\[|\Omega|-\sum_{m=1}^k(-1)^{m-1}\sum_{a_1 \sim a_m}|\bigcap_{i=1}^m\overline{S_{a_i}}| \]

现在考虑有 \(m\) 个盒子一定为空,则每个球都只有 \((k-m)\) 中选择,总共有 \((k-m)^n\) 种。

经过推导就可以得到:

\[|\Omega|-\sum_{m=1}^k(-1)^{m-1}\binom{k}{m}(k-m)^n \]

又因为 \(|\Omega|=k^n\),所以答案即为:

\[\sum_{m=0}^k(-1)^m\frac{(k-m)^{(n-1)}}{m!} \]

  • 欧拉函数

问题: 求欧拉函数 \(\varphi(n)\)

思路:

\(n = \prod_{i=1}^kp_i\)

\(S_i\):不被 \(p_i\) 整除的数。

答案即为:\(|\bigcap_{i=1}^nS_i|\)

同理,转化成求:

\[|\Omega|-\sum_{m=1}^k(-1)^{m-1}\sum_{a_1 \sim a_m}|\bigcap_{i=1}^m\overline{S_{a_i}}| \]

现在要求的即是被 \(p_{a_1}p_{a_2}\dots p_{a_m}\) 整除的数的个数,即:\(\frac{n}{p_{a_1}p_{a_2}\dots p_{a_m}}\)

所以可以变为:

\[n(\sum_{m=0}^n(-1)^m\sum_{a_1 \sim a_m}\frac{1}{\prod_{i=1}^mp_{a_i}}) \]

这个其实就是 \(n\prod_{i=1}^k(1-\frac{1}{p_i})\) 的展开。

  • 不定方程

问题: 求不定方程 \(\sum_{i=1}^nx_i=m\) 的非负解的个数,要求 \(x_i \le b_i\)

思路:

首先,如果没有性质,那就是经典问题,挡板法可知答案为 \(\binom{n+m-1}{n}\)

于是依然考虑容斥。

\(S_i\)\(x_i \le b_i\) 的方案。

答案即为:\(|\bigcap_{i=1}^nS_i|\)

同理,转化成求:

\[\sum_{a_1 \sim a_m}|\bigcap_{i=1}^m\overline{S_{a_i}}| \]

现在就是要求 \(x_{a_i} \le b_{a_i}+1\)

而我们可以先给所有的 \(x_{a_i}\) 减去 \(b_{a_i}+1\),然后就成了没有限制的挡板法,可以直接计算。

然后就做完了。

  • 互素数对

问题: \(n\) 个数 \(a_1 \sim a_n\),求数对 \((i,j)\) 个数,要求 \(a_i, a_j\) 互素。\(n \le 10^5, a_i \le 10^6\)

思路:

容斥。

\(p_1 \sim p_k\)\(10^6\) 以内的素数。

\(S_i\):所有都能整除 \(p_i\) 的数对。

答案即为:

\[n^2 - \sum_{m=1}^k(-1)^{m-1}\sum_{a_1 \sim a_m}|\bigcap_{i=1}^mS_{a_i}| \]

考虑到都在 \(10^6\) 以内,预处理出每个 \(x\) 的倍数个数,暴力搜索即可。

题目

  • P1450 [HAOI2008] 硬币购物

题目链接:P1450 [HAOI2008] 硬币购物

思路:

这道题的重点在于如何转换为容斥原理,我们把每一种使得最终和为 \(s\) 的方案(没有数量限制)看作一个元素,则假设有所有方案的集合为 \(S\),而方案中第 \(i\) 种硬币数量超出 \(d_i\) 的所有方案的集合为 \(A_i\),则我们需要求的答案其实就是:

\[|S|-|\bigcup_{i=1}^4A_i| \]

那么该如何的去求出 \(|S|\)\(|\bigcup_{i=1}^4A_i|\) 呢?

首先我们设 \(dp_i\) 表示硬币数量不受限制,最终和为 \(i\) 的方法数,这很明显是一个完全背包,由于题目中 \(s \le 10^5\) ,所以直接预处理即可,这样 \(|S|\) 就是 \(dp_s\)

void init() {
	dp[0] = 1;
	for (int i = 1; i <= 4; i++) {
		for (int j = c[i]; j <= 100000; j++)	
			dp[j] += dp[j - c[i]];
	}	
}

考虑如何求 \(|A_i|\),我们可以先取 \(d_i+1\)\(i\) 种硬币,那么还剩下 \(s-(d_i+1) \times c_i\) 的金额,再怎么取 \(i\) 的数量肯定超出限制,于是方法数即为 \(dp_{s-(d_i+1) \times c_i}\),然后就求出了 \(|A_i|\)

同理,如果 \(i\)\(j\) 都超出了限制,你们方法数也应该为 \(dp_{s-(d_i + 1) \times c_i - (d_j+1) \times c_j}\) ,三个或四个的以此类推。

这很明显是一个容斥,于是直接根据容斥原理算,只用枚举每次是哪几类超出限制即可,复杂度 \(O(2^4n)=O(16n)=O(n)\)

如果这题物品种类有 \(m\) 个,复杂度就是 \(O(n2^m)\)

  • P5505 [JSOI2011]分特产

题目链接:P5505 [JSOI2011]分特产

思路:

这道题的重点以人为单位来计数。

首先说一下可重组合,即把 \(n\) 分成 \(m\) 个非负整数集合,它们的和为 \(n\) 的方法数,我们用小学奥数的挡板法即可得到答案是:\(C_{n+(m-1)}^{m-1}\)

我们设 \(T_{i,k}\) 表示把第 \(i\) 种特产,数量为 \(a_i\),分给 \(k\) 个人的方法数(不一定每个人都要分到)。这个问题和上面其实是同一个问题,所以 \(T_{i,k}=C_{a_i+(k-1)}^{k-1}\)

\(N_k\) 为把所有特产分给 \(k\) 个人的方法数(依然有人可能没拿到),因为每个特产都要被分发,且第 \(i\) 种特产分给 \(n\) 个人的方法数是 \(T_{i,n}\),所以这是一个乘法原理,即:

\[N_k=\prod_{i=1}^mT_{i,k} \]

设集合 \(A_i\) 为第 \(i\) 名同学没有被分到特产的所有方案的集合,\(S\) 为所有人分所有特产(有人可以没分到)的方案的集合,因为我们要保证每个人都被分到,所以我们要求的就是:\(|S|-|\bigcup\limits_{i=1}^nA_i|\)

很明显 \(|S| = N_n\),考虑如何求 \(|\bigcup\limits_{i=1}^nA_i|\)。我们先考虑如果某一个同学没拿到特产的方法数(别的也不一定都拿到)应该为 \(N_{n-1}\),因为有 \(n\) 个人,所以总共是: \(C_n^1 \times N_{n-1}\)。在考虑有两个人没拿到特产的方法数,总共应该是 \(C_n^2 \times N_{n-2}\),而上面这两者是有交集的,于是根据容斥原理,我们可以以此类推,得到:

\[|\bigcup\limits_{i=1}^nA_i|=\sum_{i=1}^n(-1)^{i+1}C_n^i\times N_{n-i} \]

所以答案其实就是:

\[\sum_{i=0}^n(-1)^{i}C_n^i\times N_{n-i} \]

然后就快乐的 AC 了~~

  • P6076 [JSOI2015]染色问题

题目链接:P6076 [JSOI2015]染色问题

思路:

\(T_i\) 为有 \(i\) 种颜色确定不用,剩下的颜色随意的方法数,则根据容斥原理,我们要求的就是:

\[\sum_{i=0}^n(-1)^i\times C_n^i \times T_i \]

考虑如何求 \(T_k\)。我们记 \(N_i\) 为有 \(i\) 行确定不涂色,其他行随意的,但是每一列都有颜色的方法数,则根据容斥原理,很明显:

\[T_k = \sum_{i=0}^n(-1)^i\times C_n^i\times N_i \]

考虑如何求 \(N_i\)(别烦,这时最后一个了)。我们考虑把每一列拆开来,因为每一列有 \(n-i\) 个格子需要染色,每个格子有 \(c-k+1\) 种染色方法(不染色也算一种),所以对于一列来说,共有 \((c-k+1)^{n-i}\) 种染色方式,但是不能全部不染色,所以还要减去一,即 \((c-k+1)^{n-i}-1\)。因为总共有 \(m\) 列,并且每一列是相互独立的,于是就知道了:

\[N_i = [(c-k+1)^{n-i}-1]^m \]

然后就可以求出 \(T_k\),最后求出答案了。

在具体实现中,其实并不需要去真的开三个数组。这题除了求组合数时阶乘以及逆元需要开个数组,其他都没必要。

代码很简单,但其实思维难度还是很高的。

  • AGC005D ~K Perm Counting

题意:

给定 \(n,k\),求所有满足 \(|p_i - i| \not = k\)\(n\) 长度排列个数,\(n, k \le 2000\)

思路:

我们将每个排列一一映射到一个 \(n \times n\) 棋盘,第 \(i\) 行第 \(p_i\) 个有棋子,显然每行每列恰好一个棋子。

我们将所有不能放的位置标出来,大致是两条斜向下的斜线。这条线上的都不能放。

考虑容斥,\(f_i\) 表示放了至少 \(i\) 个再不能放的位置山,答案即为 \(\sum_{i=0}^n (-1)^i f_i(n-i)!\)

数据范围不大,可以考虑 \(dp\)

我们发现我们可以将同一行或同一列不能放的连边,可以形成若干条链,每条链相邻两个不能选。将所有链拼在一起,升个维就可以 \(dp\) 求出 \(f\) 了。

  • ARC093F Dark House

题意:

\(2^n\) 个人打淘汰赛,共 \((2^n)!\) 中顺序,你是 \(1\) 号,有 \(M\) 个人你打不过,其他人都打得过。而除了你之外的人编号小的更强。求有多少中顺序满足你能获得第一。

\(n \le 16, m \le 16\)

思路:

首先,由于比赛是对称的,不妨假定 \(1\) 号初始在第 \(1\) 个位置,最后乘上 \(n\) 即可。

我们可以发现,\(1\) 号需要战胜的是 \(n\) 组选手的最小值,也就是到根节点路径上每个节点的另一个子树的所有点。

我们考虑如何求得方案使得这 \(m\) 个人都不会在组里获胜。

我们考虑子集反演,设 \(f(S)\) 表示 \(S\) 中组都是打不过的人获胜,其其他组都不是。\(g(S)\) 表示 \(S\) 中组都是打不过的人获胜,其他组任意。显然会有:

\[g(S) = \sum_{S \sub T}f(T) \]

反演得到:

\[f(S) = \sum_{S \sub T}(-1)^{|T|}g(T) \]

答案就是 \(f(\empty)\)

这样就只用求 \(g(T)\) 了。

考虑 dp,设 \(dp[i][S]\) 表示填了前 \(i\) 个打不过的,已经填了组的集合是 \(S\),有多少种方案。

这种类似匹配的经典技巧是将 \(a_1 \sim a_m\) 从大到小处理,然后可以快速计算每个人有多少个剩余填法。

这样 \(g(S)\) 就等于 \(dp[m][S] \times (2^n - 1 - tot(S))\),其中 \(tot(S)\) 表示 \(S\) 所占的位置总个数。

很好的子集反演和 dp 题。

考虑容斥,我们将 \(|P_i - i| < x\) 视作每个 \(i\) 的性质,然后我们算满足 \(k\) 个性质的排列有多少,最后容斥一下即可。

由于数据范围很小,显然是 DP,我们将 \(p_i\) 置换,即 \(q_{p_i} = i\),然后限制就是我们要求有 \(k\) 个数取值在区间 \([i - x + 1, i + x - 1]\),其它随便,不能重叠。由于区间长度很小,考虑状压。设 \(dp_{i,j,S}\) 表示前 \(i\) 个填了 \(j\) 个,用掉了 \([i - x + 1, i + x - 1]\) 中的位置集合为 \(S\) 的方案数。转移非常简单。

然后就做完了。

显然容斥,\(f(i)\) 表示至少 \(i\) 个元素出现次数小于等于 \(1\) 次,答案就是 \(\sum(-1)^kf(k)\)

考虑如何计算 \(f(k)\),首先我们需要选出 \(k\) 个,这是 \(\binom{n}{k}\)

然后我们考虑这 \(k\) 个,将这 \(k\) 个分成若干个非空集合,使得每个元素出现次数不超过 \(1\) 次。

不妨设 \(s_{i,j}\) 表示将 \(i\) 个有区别的东西放入 \(j\) 个无区别的盒子中,可以有些东西不放,盒子非空,求方案数。

这个看着很想第二类斯特林数,其转移方程与其类似:\(s_{i,j} = (j+1)s_{i - 1,j} + s_{i-1,j-1}\)

于是我们枚举分了 \(m\) 组,这 \(k\) 个数的方案数就是 \(s_{k, m}\)

再考虑剩下的,不包含这 \(k\) 个数的集合有 \(2^{n-k}\),我们都可以选或不选,有 \(2^{2^{n-k}}\) 种。

对于这 \(m\) 个集合,每个集合对于其他的 \(n-k\) 个也是可以选和不选,有 \((2^{n-k})^m\) 种。

所以我们就得出了 \(f(k)\) 的计算公式:

\[f(k) = \binom{n}{k}\sum_{m=0}^{k}s_{k, m}2^{2^{n-k}}(2^{n-k})^m \]

最终复杂度 \(O(n^2 \log n)\),不要忘了对指数需要模 \(M - 1\)

经典结论:找重心,然后任意一对点不在同一棵子树中。

如果有两个重心,答案就是 \((\frac{n}{2}!)^2\)

否则,我们把重心拎出来,将 \(i\) 是否与 \(p_i\) 属于同一儿子的子树作为条件来容斥。

\(f(i)\) 表示至少有 \(i\) 个属于同一儿子的子树,答案即为 \(\sum (-1)^kf(k)(n-k)!\)

对于一个大小为 \(m\) 的子树,选出 \(j\) 个数所对应的数的方案数是 \(\binom{m}{j}^2j!\)

所以我们可以用一个背包求出 \(f(i)\)

这道题重点在于找到适合的条件容斥。

DP 中带容斥系数

在一类题中,我们需要用 dp 求答案,最后再容斥算出答案,这样复杂度与 dp 有关。

但是我们也可以将容斥系数直接套进 dp 里,这样可以减少一维状态。

题意: 一棵树,但是边有方向,求拓扑序方案数。

思路:

如果这棵树是内向树或外向树,显然我们可以用 dp 求解。

所以我们考虑计算外向树,然后把所有不符合条件的边容斥掉。不妨设 \(f_{i,j,k}\) 表示 \(i\) 的子树内 \(j\) 条边由不符合反转成符合,\(i\) 所在连通块大小为 \(k\)

显然是 \(O(n^3)\),我们发现最后的答案是 \(\sum (-1)^jf_{rt, j, k}\),所以我们可以考虑将 \(j\) 这一维代入 dp 值里。

\(g_{i,k} = \sum (-1)^j f_{i,j,k}\),则我们考虑对 \(g\) 进行 dp。

我们可以先把 \(f\) 的转移写出来:

如果这条边符合:

\[\binom{k + k' - 1}{k'}f(x,j,k)f(y,j',k') \to F(x,j + j', k + k') \]

如果不符合:

\[\binom{k + k' - 1}{k'}f(x,j,k)f(y,j',k') \to F(x,j + j' + 1, k + k') \]

\[\frac{1}{k'!}f(x,j,k)f(y,j',k') \to F(x,j + j', k) \]

发现这就是类似卷积的形式,于是我们可以将 \(g\) 的转移写出来:

\[\binom{k + k' - 1}{k'}g(x,k)g(y,k') \to G(x,k + k') \]

\[(-1)\binom{k + k' - 1}{k'}g(x,k)g(y,k') \to G(x,k + k') \]

\[\frac{1}{k!}g(x,k)g(y,k') \to G(x,k) \]

然后我们就做完了。

题意: 一棵树,求多少种方案将顶点两两配对,使得将每个点对的路径上的所有边全部系上丝带后满足,每条边都有至少一条丝带。

思路:

\(n\) 个点两两配对的方案数是所有小于 \(n\) 的奇数的乘积。这个很容易就可以推出来。

如果我们钦定哪些边不覆盖,方案数很好计算,所以我们考虑容斥,\(F(i,j,k)\) 表示 \(i\) 子树内,有 \(j\) 条边未覆盖,其他任意,当前与 \(i\) 联通的点有 \(k\) 个,求方案数。

接着就是和上道题几乎一样的容斥与转移。

约数容斥

约数容斥主要解决环计数相关问题。

约数容斥基于以下事实:

\[g(i) = \sum_{j|i}f(j) \implies f(i) = g(i) - \sum_{j|i, j \not = i}f(j) \]

甚至不需要证明,只是将 \(f(i)\) 单独领出来而已。

计算这类问题,加上记忆化后时间复杂度是 \(O(n^{\frac{2}{3}})\)

题意: 生成一个长度为 \(n\) 的序列,过程如下:先生成一个 \(n\) 长度且每个元素 \(1 \sim k\) 的回文串,然后进行若干次将第一个元素移到最后,求有多少种不同的序列。\(n \le 10^9\)

思路:

环计数需要考虑最小循环节。不妨设 \(f(i)\) 表示最小循环节长度为 \(i\) 的个数,\(g(i)\) 表示循环节为 \(g(i)\) 的个数。

观察题目性质,发现回文串的循环节一定是回文串。

所以我们可以得到 \(g(i) = k^{\lceil\frac{i}{2}\rceil}\),进而可以通过约数容斥计算出 \(f(i)\)

考虑统计答案,\(i\) 为奇数时可以贡献 \(i \times f(i)\),否则贡献 \(\frac{i}{2} \times f(i)\)

然后就做完了。一定记得需要用 map 实现记忆化。

  • [无题号]Aku no Hanabira

题意:\(K\) 种颜色球,每种颜色 \(c_i\) 个,将这些球排成一个圆,求有多少本质不同的圆?(只考虑旋转,不考虑翻折)\(K \le 10^5, n = \sum_{c_i} \le 10^6\)

思路:

依然是环计数,容易发现计算 \(g(i)\) 就是计算多重集的排列,可以在 \(O(Kn^{\frac{2}{3}})\) 解决。

点击查看代码
#include <iostream>
#include <vector>
#include <map>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e6 + 5;
const int mod = 1e9 + 7;

int k, c[N] = {0};

int fpow(int a, int b, int p) {
	if (b == 0)
		return 1;
	int ans = fpow(a, b / 2, p);
	ans = 1ll * ans * ans % p;
	if (b % 2 == 1)
		ans = 1ll * a * ans % p;
	return ans;
}	

int fac[N] = {0}, inv[N] = {0};
void init(int n) {
	fac[0] = 1;
	for (int i = 1; i <= n; i++)
		fac[i] = 1ll * fac[i - 1] * i % mod;
	inv[n] = fpow(fac[n], mod - 2, mod);
	for (int i = n - 1; i >= 0; i--)
		inv[i] = 1ll * inv[i + 1] * (i + 1) % mod;
}

int n;
int g(int m) {//循环节长度为 m 
	int ans = fac[m];
	for (int i = 1; i <= k; i++)
		if (c[i] % (n / m) != 0)
			return 0;
		else
			ans = 1ll * ans * inv[c[i] / (n / m)] % mod;
	return ans;
} 

int gcd(int a, int b) {
	return a == 0 ? b : gcd(b % a, a); 
}

map<int, int> mp;

int f(int m) {
	if (mp.count(m))
		return mp[m];
	int ans = g(m);
	for (int i = 1; i * i <= m; i++)
		if (m % i == 0) {
			if (i != m)
				ans = (ans - f(i) + mod) % mod;
			if (i * i != m && i != 1)
				ans = (ans - f(m / i) + mod) % mod;
		} 
	return mp[m] = ans;
}



int main() {
	cin >> k;
	n = 0;
	for (int i = 1; i <= k; i++)
		cin >> c[i], n += c[i];
	init(n);
	f(n);
	int ans = 0;
	for (int i = 1; i <= n; i++)
		ans = (ans + 1ll * mp[i] * fpow(i, mod - 2, mod) % mod) % mod;
	cout << ans << endl;
	return 0;
}

Min-max 容斥

公式:

\[\max_{i \in S}x_i=\sum_{T \subset S,T \not = \emptyset}(-1)^{|T|-1}\min_{j \in T}x_j \]

\[\min_{i \in S}x_i=\sum_{T \subset S,T \not = \emptyset}(-1)^{|T|-1}\max_{j \in T}x_j \]

证明:

定义 \(f(n)=\{x|1 \le x \le n, x \in \mathbb{Z}\}\),则 \(f(\max(x,y))=f(x) \bigcup f(y)\)\(f(\min(x,y))=f(x) \bigcap f(y)\)

于是上面的式子和容斥原理其实是等价的。

首先这东西看上去没啥用,但是它最重要的是对期望也成立:

\[E(\max_{i \in S}x_i)=\sum_{T \subset S,T \not = \emptyset}(-1)^{|T|-1}E(\min_{j \in T}x_j) \]

于是这就可以解决一些期望问题。

题目1: 【Card Collector】hdu-4336

题目2: 【随机游走 (PKUWC’18)】loj-2542

广义容斥原理

全集 \(\Omega\) 中有若干个元素,有 \(n\) 种性质,\(A_i\) 表示满足第 \(i\) 个性质的元素的集合。

\(\beta_k\) 表示恰好满足 \(k\) 中性质的元素集合。

定义 \(\alpha_k=\sum_{i_1 < i_2 < \dots < i_k}|A_{i_1} \cap A_{i_2} \cap \dots \cap A_{i_k}|\)

\(\alpha_k = \sum_{i=k}^n \binom{i}{k}\beta_i\)

理解:

我们考虑实际满足了 \(i\) 种性质的元素在 \(\alpha_k\) 中被算了 \(\binom{i}{k}\) 次,所以这个式子成立。

作用:

根据二项式反演公式3(见二项式反演学习笔记),如果 \(\alpha_k\) 好算,那么我们我们可以反演用 \(\alpha_k\) 求出 \(\beta_k\)

题目1: 有两个序列 \(a_1 \sim a_n\)\(b_1 \sim b_n\),数字各不相同,求匹配方案数,使得 \(a_i > b_{p_i}\) 的个数恰好为 \(k\)\(n \le 5000\)P4859 已经没有什么好害怕的了

思路:

我们让性质 \(i\) 表示 \(a_i > b_{p_i}\),则我们要求的就是 \(\beta_k\),根据广义容斥原理,我们如果能求出 \(\alpha_k\) 就能解决问题。

考虑如何求 \(\alpha_k\)。数据范围支持 \(O(n^2)\),考虑 dp。

首先需要对两个数组排序。

不妨设 \(f[i][j]\) 表示前 \(i\)\(a\) 中匹配了 \(j\) 个使得满足 \(a_i > b_{p_i}\),所以 \(\alpha_k = (n-k)!f[n][k]\)

考虑递推,按照第 \(i\) 个分类,可得到 \(f[i][j] = f[i - 1][j] + (cnt[i] - j + 1)f[i - 1][j - 1]\)\(cnt[i]\) 表示有多少个 \(b\) 小于了 \(a_i\)

第一项好理解,第二项就是 \(i\) 要满足性质,则有 \(cnt[i]\) 个选择,但是前面已经占了 \(j-1\) 个,所以总共只有 \(cnt[i] - (j-1)= cnt[i]-j+1\) 种。

然后就能解决这道问题了。

posted @ 2024-01-20 13:44  rlc202204  阅读(23)  评论(0编辑  收藏  举报