计数

基本概念

容斥

容斥解决满足多条件的组合方案数问题,将各个条件集合的形式表达,即可套用容斥原理。

  • \(\left| U \right|\) :全集。

  • \(\left| S_i \right|\) :满足条件 \(S_i\) 的方案数。

  • \(\left| \bigcup_{i=1}^{n} S_i \right|\) :满足任一条件的方案数。

  • \(\left| \bigcap_{i=1}^{n}S_i \right|\) :满足全部条件的方案数。

两个公式:

  • \[\left| \bigcup_{i=1}^{n} S_i \right|=\sum_{j=1}^{n}(-1)^{j-1}\times \sum_{a_i<a_{i+1}}\left| \bigcap_{i=1}^{j}S_{a_i} \right| \]

  • \[\left| \bigcap_{i=1}^{n}\right| S_i=\left| \bigcup \right|-\left| \bigcup_{i=1}^{n}\bar{S_i} \right| \]


二项式反演

二项式反演一般用于转化恰好至多(少)之间的数量关系。

常见形式:

  • \[f_i=\sum_{i=0}^{n}(-1)^i\;\times \;C_{n}^{i}\;\times\;g_i\iff g_i=\sum_{i=0}^{n}(-1)^i\;\times \;C_{n}^{i}\;\times\;f_i \]

  • \[f_n=\sum_{i=0}^{n}C_n^i\times g_i \iff g_n=\sum_{i=0}^{n}(-1)^{n-i}\times C_n^i\times f_i \]

高维二项式反演:

  • \[f_{x,y}=\sum_{i=x}^{n}\sum_{j=y}^{m}C_i^xC_j^y\times g_{i,j}\iff g_{x,y}=\sum_{i=x}^n\sum_{j=y}^m(-1)^{i+j-x-y}C_i^xC_j^y\times f_{i,j} \]

  • 剩下以此类推。

恰好与至多的转化

\(f_k\) 表示至多满足 \(k\) 个条件,\(g_k\) 满足恰好 \(k\) 个条件,由二项式反演,得:

\[f_k=\sum_{i=0}^{k}C_k^i\times g_i\\ g_k=\sum_{i=0}^{k}\times (-1)^{k-i}\times C_k^i \times f_i \]

恰好与至少的转化

\(f_k\) 为至少满足 \(k\) 个条件,\(g_k\) 满足恰好 \(k\) 个条件,由二项式反演,得:

\[f_k=\sum_{i=k}^{n}C_i^k\times g_i\\ g_k=\sum_{i=k}^{n}(-1)^{i-k}\times C_i^k\times f_i \]

如果题目要求恰好的数量,要么直接求(DP)很好求,要么间接求再用二项式反演求很好求。

错排问题

问题:某人写了 \(n\) 封信和 \(n\) 个信封,如果所有的信都装错了信封。求所有信都装错信封共有多少种不同情况。

证明略。递推式如下:

\[f_1=0,f_2=1\\ f_n=(n-1)\times(f_{n-1}+f_{n-2})\;\;\;\;\;\;(n \ge 3) \]


整除分块

一般解决形如一下的问题:

\[\sum_{i=0}^{n}\left\lfloor \frac{n}{i} \right\rfloor \]

可以证明,在第一个使 \(\left\lfloor \frac{n}{i} \right\rfloor\) 取不同值的 \(i\) 时,

\[i\sim \left\lfloor \frac{n}{\left\lfloor \frac{n}{i} \right\rfloor} \right\rfloor \]

时,\(\left\lfloor \frac{n}{i} \right\rfloor\) 的取值相等。

又可以证明,这样的块不超过 \(2\sqrt{n}\) 个,于是可以在根号时间复杂度内算出值。

题目选做

数论相关

1. H(n)

整除分块模板题。

#include <cstdio>
#include <iostream>

using namespace std;

typedef long long LL;
int T; 
LL n;

int main () {
	cin >> T;
	while(T --) {
		cin >> n;
		LL ans = 0;
		for (LL l = 1, r; l <= n; l = r + 1) {
			r = min(n, n / (n / l));
			ans += (n / l) * (r - l + (LL)1);
		}
		cout << ans << endl;
	}
	return 0;
}

2. Calculating

由算数基本定理可知,\(f(x)\) 就是 \(x\) 的约数个数。将要求的式子转化为:

\[\sum_{i=0}^{r}f(i)-\sum_{i=0}^{l-1}f(i) \]

其中,\(\sum_{i=0}^{r}f(i)\) 等价于 \(\sum_{i=0}^{r}\left\lfloor \frac{n}{i} \right\rfloor\) ,可以使用整除分块。

3.[POI2007]ZAP-Queries

求以下式子:

\[\sum_{x=1}^{a}\sum_{y=1}^{b}(gcd(x, y)==d) \]

转化为:

\[记\;a'= \left\lfloor \frac{a}{d} \right\rfloor ,\;b'=\left\lfloor \frac{b}{d} \right\rfloor\\ \sum_{x=1}^{a'}\sum_{y=1}^{b'}(gcd(x,y)==1) \]

正难则反,可以用容斥的思想做,转化为:

\[\left\lfloor \frac{a'}{1} \right\rfloor\left\lfloor \frac{b'}{1} \right\rfloor-\left\lfloor \frac{a'}{2} \right\rfloor\left\lfloor \frac{b'}{2} \right\rfloor-\left\lfloor \frac{a'}{3} \right\rfloor\left\lfloor \frac{b'}{3} \right\rfloor-...+\left\lfloor \frac{a'}{6} \right\rfloor\left\lfloor \frac{b'}{6} \right\rfloor... \]

即:

\[\sum_{i=1}^{min(a',b')} \mu(i) \left\lfloor \frac{a'}{i} \right\rfloor\left\lfloor \frac{b'}{i} \right\rfloor \]

莫比乌斯函数即为容斥系数。

因为这个式子就是整除分块的基本式,所以将 \(\mu(i)\) 做一个前缀和就可以用整除分块。

莫比乌斯函数预处理过程:

void prework() {
	mul[1] = 1;
	for (int i = 2; i <= M; i ++) {
		if (!f[i]) {
			prime[++tot] = i;
			mul[i] = -1;
		}
		for (int j = 1; j <= tot && i * prime[j] <= M; j ++) {
			LL k = i * prime[j];
			f[k] = 1;
			if (i % prime[j] == 0) {
				mul[k] = 0;
				break;
			}
			else mul[k] = mul[i] * -1;
		}
	}
	for (int i = 1; i <= M; i ++) sum[i] = sum[i - 1] + mul[i];//前缀和
}

4. [HAOI2011]Problem b

求下列式子的值:

\[\sum_{x=a}^{b}\sum_{y=c}^{d}(gcd(x, y)==k) \]

通过简单容斥,转化为:

\[记 K=(gcd(x,y)==k)\\ \sum_{x=1}^{b}\sum_{y=1}^{d}K-\sum_{x=1}^{b}\sum_{y=1}^{c-1}K-\sum_{x=1}^{a-1}\sum_{y=1}^{d}K+\sum_{x=1}^{a-1}\sum_{y=1}^{c-1}K \]

类似于矩阵前缀和的形式,可以画出矩阵更加直观的理解。

那么问题就转化成了求

\[\sum_{x=1}^{n}\sum_{y=1}^{m}(gcd(x, y)==k) \]

的值,就是上一题

5.四元组统计

正难则反。所有四元组有 \(C_{n}^{4}\) 个,用容斥减去不合法的方案数,也就是 \(gcd\;!=1\) 的方案数,设 \(f(i)\) 表示至少 \(gcd=i\) 的四元组数,答案即为:

\[Ans=\sum_{i=1}^{max}\mu(i)\times f(i) \]

考虑求出 \(f(i)\) ,可以统计因数 \(i\) 在数列每个数中是否出现,记 \(sum(i)\) 表示因数 \(i\) 出现在数列中数字的个数,那么:

\[f(i)=C_{sum(i)}^{4} \]

考虑求出 \(sum(i)\) ,暴力一点的想法就是直接枚举每个数的因数,时间复杂度为 \(O(Tn \sqrt a)\),极限数据恰好是1e8,可以通过此题。

类似于素数筛法,可以利用筛法的思想将每一个数的因数筛出来,这样可以大大提高代码运行效率。

Ps:双倍经验:MSKYCODE - Sky Code

6. NGM2 - Another Game With Numbers

很容易联想到容斥原理,可以用 \(dfs\) 搜出所有的组合,然后做容斥。

code

#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

typedef long long LL;

LL n, k, ans;
int f[20], b[20];
vector<int> a;

inline LL gcd(LL a, LL b) { return b == 0?a:gcd(b, a%b); }
inline LL lcm(LL a, LL b) { return a / gcd(a, b) * b; }
inline void dfs(int nd, int m, LL s, int p) {
	if (s > n) return;
	if (nd == m + 1) {
		ans += n / s * p;
		return ;
	}
	
	for (int i = b[nd - 1] + 1; i <= k; i ++) {
		LL lll = lcm(s, a[i - 1]);
		b[nd] = i;
		dfs(nd + 1, m, lll, p);
	}
}

int main() {
	ios::sync_with_stdio(0);
	cin >> n >> k;
	for (int i = 1; i <= k; i ++) {
		int x;
		cin >> x;
		a.push_back(x);
	}
	int p = 1;
	for (int i = 0; i < k; i ++) {
		memset(b, 0, sizeof(b));
		dfs(0, i, 1, p);
		p *= -1;
	}
	
	cout << n - ans << endl;
	return 0;
}

7. Devu and Birthday Celebration

考虑枚举 \(n\) 的因数 \(x\) ,将 \(\frac{n}{x}\) 拆分成 \(f\) 份,那么这样拆分出的 \(a_i\) 必然满足有 \(x\) 这个因数。

然后考虑容斥,在根号时间内枚举 \(n\) 的因数,然后用莫比乌斯函数做容斥系数。即:

\[Ans=\sum_{x|n}\mu(x)\times \dbinom{\frac{n}{x}}{f} \]

8.SQFREE - Square-free integers

直接容斥,求式子:

\[\sum_{i^2\le n}\mu(i)\times \frac{n}{i^2} \]

9. List Of Integers

先来考虑这样一个问题:求 \(1\sim x\) 有多少个数与 \(p\) 互质。

问题可以转化为求如下式子的值:

\[\sum_{i=1}^{x}(gcd(i,p)==1) \]

这个式子是我们的老熟人了,直接套容斥。即求如下式子的值:

\[\sum_{a|n} \mu(a)\times \left\lfloor \frac{a}{i}\right\rfloor \]

回到原问题,我们可以二分要求的数,那么 \(check\) 函数就是如上问题。

code

#include <cstdio>
#include <iostream>
#include <algorithm>

using namespace std;

#define R register
const int N = 1e6 + 10;
const int MAX = 1e7;
const int M = 1e6;
typedef long long LL;
int T;
LL x, p, k;
int f[N];
LL cnt, ys[N];
LL mul[N], prime[N], tot;

inline void init() {
	mul[1] = 1;
	for (R int i = 2; i <= M; i ++) {
		if (!f[i]) {
			prime[++tot] = i;
			mul[i] = -1;
		}
		for (R int j = 1; j <= tot && i * prime[j] <= M; j ++) {
			LL t = i * prime[j];
			f[t] = 1;
			if (i % prime[j] == 0) {
				mul[t] = 0;
				break;
			}
			mul[t] = mul[i] * -1;
		}
	}
}

inline LL check(LL k, LL p) {
	LL ans = 0;
	for (R int i = 1; i <= cnt; i ++) {
		ans += mul[ys[i]] * (k / ys[i]);
	}
	return ans;
}

int main() {
	ios::sync_with_stdio(0);
	
	init();
	
	cin >> T;
	while (T --) {
		cin >> x >> p >> k;
		
		cnt = 0;
		for (R int i = 1; i * i <= p; i ++) {
			if (p % i == 0) {
				ys[++cnt] = i;
				if (i * i != p) ys[++cnt] = p / i;
			}
		}
		
		
		LL ans_x = check(x, p);
		LL l = x, r = MAX, ans = 0;
		
		while (l <= r) {	
			LL mid = l + r >> 1;
			if (check(mid, p) - ans_x >= k) ans = mid, r = mid - 1;
			else l = mid + 1;
		}
		
		cout << ans << endl;
	}
	return 0;
}

DP相关

1. [JSOI2011]分特产

正难则反。

\(f_i\) 表示钦定(至少)前 \(i\) 个人没有分到特产,其他的放任自流的方案数。

\(g_i\) 表示恰好 \(i\) 个人没有分到特产的方案数。

易得,\(g_0\) 就是答案,由二项式反演,得:

\[g_0=\sum_{i=0}^{n}(-1)^{i-0}\times \dbinom{n}{i}\times f_i \]

考虑如何求 \(f\) 数组。

要求 \(f_i\) ,只需考虑剩下 \(n-i\) 个人的取特产情况,考虑将每个特产依次分给这 \(n-i\) 个人,由于不一定每个人都要分到,所以问题就等价于允许有空盒不同盒子相同球的模型,于是得到如下式子:

\[f_i=\prod_{j=1}^{m} \dbinom{a_j+n-i-1}{n-i-1} \]

然后再用上面的式子求出 \(g_0\) 即为答案。

2.Cheerleaders

正难则反。

\(f_i\) 表示钦定 \(i\) 条边没有人占,其余放任自流的方案数。

容易得出全集即为 \(f_0=\dbinom{n\times m}{k}\)

考虑求出 \(f\) 数组,

\(i=1\) 时,除去一条边不能占,那么就有 \(m\times(n-1)\) 或者 \(n\times (m-1)\) 的区域是爱占不占的,于是:

\[f_1=2\times\dbinom{m\times(n-1)}{k}+2\times\dbinom{n\times (m-1)}{k} \]

\(i=2\) 时,除去两条边不能占,那么就有 \(n\times(m-2)\)\(m\times(n-2)\)\((n-1)\times (m-1)\) 的区域是爱占不占的,于是:

\[f_2=\dbinom{n\times(m-2)}{k}+ \dbinom{m\times(n-2)}{k} + 2\times\dbinom{(n-1)\times(m-1)}{k} \]

同理可得,当 \(i=3\)\(i=4\) 的方案数:

\[f_3=2\times\dbinom{(n-1)\times(m-2)}{k} + 2\times\dbinom{(n-2)\times(m-1)}{k}\\ f_4=\dbinom{(n-2)\times(m-2)}{k} \]

套用容斥可知答案为:

\[Ans=f_0-f_1+f_2-f_3+f_4 \]

3. [JSOI2015]染色问题

正难则反。

咱这有个不需要脑子的二项式反演做法。。。

\(f_{i,j,k}\) 表示钦定 \(i\) 行,\(j\) 列一个没染,\(k\) 个颜色一个没用,其余放任自流的方案数。

\(g_{i,j,k}\) 表示恰好 \(i\) 行,\(j\) 列一个没染,\(k\) 个颜色一个没用的方案数。

易得:

\[f_{i,j,k}=\dbinom{n}{i}\dbinom{m}{j}\dbinom{c}{k}\times (c-k+1)^{(n-i)(m-j)} \]

再由高维二项式反演得:

\[f_{x,y,z}=\sum_{i=x}^{n}\sum_{j=y}^{m}\sum_{k=z}^{c}\dbinom{n}{i}\dbinom{m}{j}\dbinom{c}{k}\times g_{x,y,z}\\ g_{x,y,z}=\sum_{i=x}^{n}\sum_{j=y}^{m}\sum_{k=z}^{c}(-1)^{i+j+k-x-y-z}\times\dbinom{i}{x}\dbinom{j}{y}\dbinom{k}{z}\times f_{i,j,k}\\ \]

由于答案就是 \(g_{0,0,0}\) ,所以:

\[\begin{aligned} &g_{0,0,0}=\sum_{i=0}^{n}\sum_{j=0}^{m}\sum_{k=0}^{c}(-1)^{i+j+k}\times\dbinom{n}{0}\dbinom{m}{0}\dbinom{c}{0}\times f_{i,j,k}\\ &\;\;\;\;\;\;\;\;=\sum_{i=0}^{n}\sum_{j=0}^{m}\sum_{k=0}^{c}(-1)^{i+j+k}\times\dbinom{n}{0}\dbinom{m}{0}\dbinom{c}{0}\times\dbinom{n}{i}\dbinom{m}{j}\dbinom{c}{k}\times (c-k+1)^{(n-i)(m-j)}\\ &\;\;\;\;\;\;\;\;=\sum_{i=0}^{n}\sum_{j=0}^{m}\sum_{k=0}^{c}(-1)^{i+j+k}\times\dbinom{n}{i}\dbinom{m}{j}\dbinom{c}{k}\times (c-k+1)^{(n-i)(m-j)} \end{aligned} \]

若直接求这个式子的值,时间复杂度为 \(O(nmc\times log(nm))\) ,非常悬。这类柿子一般用二项式定理优化,发现 \((c-k+1)\) 的指数 \((n-i)(m-j)\)\(k\) 一点关系没有,于是将 \(\sum_{k=0}^{c}\) 拉到最外面枚举,得:

\[g_{0,0,0}=\sum_{k=0}^{c}\sum_{i=0}^{n}\sum_{j=0}^{m}(-1)^{i+j+k}\times\dbinom{n}{i}\dbinom{m}{j}\dbinom{c}{k}\times (c-k+1)^{(n-i)(m-j)}\\ g_{0,0,0}=\sum_{k=0}^{c}(-1)^k\dbinom{c}{k}\sum_{i=0}^{n}(-1)^i\dbinom{n}{i}\sum_{j=0}^{m}(-1)^j\dbinom{m}{j}\times (c-k+1)^{(n-i)(m-j)}\\ \]

由二项式定理,得:

\[g_{0,0,0}=\sum_{k=0}^{c}(-1)^k\dbinom{c}{k}\sum_{i=0}^{n}(-1)^i\dbinom{n}{i}\times ((c-k+1)^{n-i}-1)^m \]

时间复杂度降为 \(O(nc\times log(m))\)

4.Sky Full of Stars

正难则反。。。

咱又有一个不需要脑子的高维二项式反演做法。。。

\(f_{i,j}\) 表示钦定 \(i\)\(j\) 列颜色相同,其余的放任自流的方案数。

\(g_{i,j}\) 表示恰好 \(i\)\(j\) 列颜色相同的方案数。

那么 \(Ans=\left| \bigcup \right|-g_{0,0}\)\(\left| \bigcup \right|=3^{n\times n}\)

由二维二项式反演可得:

\[\begin{aligned} &g_{0,0}=\sum_{i=0}^{n}\sum_{j=0}^{n}(-1)^{i+j}\times \dbinom{i}{0}\dbinom{j}{0}\times f_{i,j}\\ &\;\;\;\;\;\;=\sum_{i=0}^{n}\sum_{j=0}^{n}(-1)^{i+j}\times f_{i,j} \end{aligned} \]

考虑求出 \(f\) 数组。

有一个显然的性质:如果至少有 \(1\) \(1\) 列满足题目要求,那么满足要求的这些行列一定是同种颜色的。如果只有行满足要求,那么行之间就不一定要同种颜色;列也是同理。

于是很快可以求出:

\[f_{i,0}=\dbinom{n}{i}\times 3^i\times 3^{n(n-i)}\\ f_{0,j}=\dbinom{n}{j}\times 3^j\times 3^{n(n-j)}\\ f_{i,j}=\dbinom{n}{i}\dbinom{n}{j}\times 3\times 3^{(n-i)(n-j)}\;\;\;\;\;\;(i\ge1,j\ge1) \]

由于 \(f_{i,0}\)\(f_{0,j}\) 都可以在线性时间内求出来,而 \(f_{i,j}\) 不行,所以将 \(f_{i,j}\) 单独拎出来考虑。

\[设\;s=\sum_{j=1}^{n}(-1)^{0+j}\times f_{0,j} + \sum_{i=1}^{n}(-1)^{i+0}\times f_{i,0} \]

\[\begin{aligned} &g_{0,0}=\sum_{i=1}^{n}\sum_{j=1}^{n}(-1)^{i+j}\times f_{i,j}+s+f_{0,0}\\ &\;\;\;\;\;\;=\sum_{i=1}^{n}\sum_{j=1}^{n}(-1)^{i+j}\times \dbinom{n}{i}\dbinom{n}{j}\times 3\times 3^{(n-i)(n-j)}+s+f_{0,0}\\ &\;\;\;\;\;\;=3\times \sum_{i=1}^{n}(-1)^i\times\dbinom{n}{i}\times \sum_{j=1}^{n}\times \dbinom{n}{j}\times(3^{n-i})^{n-j}\times (-1)^j+s+f_{0,0}\\ &\;\;\;\;\;\;=3\times \sum_{i=1}^{n}(-1)^i\times\dbinom{n}{i}\times ((3^{n-i}-1)^n-(3^{n-i})^{n})+s+f_{0,0} \end{aligned} \]

注意到 \(f_{0,0}=3^{n\times n}\) ,所以 \(Ans=-3\times \sum_{i=1}^{n}(-1)^i\times\dbinom{n}{i}\times ((3^{n-i}-1)^n-(3^{n-i})^{n})+s\)

code

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

typedef long long LL;
const LL MOD = 998244353;
const int N = 1e6 + 10;
const int M = 1e6 + 10;

LL n, sum1, sum;
LL fac[N];

LL ksm(LL a, LL b) {
	LL sum = 1;
	while (b) {
		if (b & 1) sum = sum * a % MOD;
		a = a * a % MOD;
		b >>= 1;
	}
	return sum;
}

void init() {
	fac[0] = 1;
	for (LL i = 1; i <= N - 10; i ++) fac[i] = fac[i - 1] * i % MOD;
}

LL C(LL n, LL m) {
	if (n < m) return 0;
	LL fz = fac[n], fm = fac[m] * fac[n - m] % MOD;
	return fz * ksm(fm, MOD - 2) % MOD;
}

int main() {
	init();
	cin >> n;
	
	int p = -1;
	for (int i = 1; i <= n; i ++) {
		sum1 += p * C(n, i) * ksm(3, i) % MOD * ksm(3, n * (n - i)) % MOD;
		sum1 %= MOD;
		p *= -1;
	}
	sum1 = (sum1 * 2 % MOD);

	p = -1;
	for (LL i = 1; i <= n; i ++) {
		sum += p * C(n, i) * (ksm(ksm(3, n - i) - 1, n) - ksm(3, n * (n - i))) % MOD;
		sum = (sum % MOD + MOD) % MOD;
		p = p * -1;
	}
	
	sum = (3 * sum + sum1) % MOD;
	cout << (-sum + MOD) % MOD;
	return 0;
}

PS:\(f_{i,0}\) 也可以进行二项式反演优化,但是我懒。。。

posted @ 2022-11-22 09:40  2017BeiJiang  阅读(92)  评论(0编辑  收藏  举报