容斥原理

容斥原理是解决这一类问题的:有 \(N\) 个集合 \(S_1,S_2,\dots,S_N\),求 \(|\bigcup \limits_{i=1}^{N}S_i|\)

我们就能发现,所有偶数个集合的交集前面都是减号,而奇数的前面的都是加号,也就是:

\[|\bigcup \limits_{i=1}^N S_i|=\sum \limits_{k=1}^N (-1)^{k-1} \sum \limits_{1\le i_1<i_2<\dots<i_k\le N}|\bigcap \limits_{j=1}^k S_{i_j}| \]

(具体证明在[这里](容斥原理_百度百科 (baidu.com)))

代码

for(int i = 1; i < (1 << n); ++i) {
  ans += (__builtin_popcount(i) % 2 ? 1 : -1) * /* i 代表的集合之交 */;
}

HDU 6397

题目描述

给定 \(N,M,K\),求有多少个长度为 \(M\) 的序列 \(A\) 满足 \(0\le A_i < N 且 \sum \limits_{i=1}^M A_i = K\)

思路

由于没有 \(A_i\) 的范围限制就可以直接插板法,所以考虑去除非法情况。

我们可以考虑求出有 \(x\) 个元素 \(\ge N\) 的情况数量,可以看作是有 \(x\) 个元素预先分配了 \(N\),也就是 \(C_{M}^{x}\cdot C_{K + M-1-x\cdot N}^{M-1}\) 种情况,第一个 \(C\) 是从 \(M\) 个数种选出 \(x\) 个,第二个部分是用插板法计算数量。

接着使用容斥原理求解即可。

空间复杂度 \(O(1)\),时间复杂度 \(O(M)\)

代码

#include<bits/stdc++.h>
using namespace std;
using ll = long long;

const int MAXN = 200001, MOD = 998244353;

int t, n, m, k, inv[MAXN], f[MAXN], Inv[MAXN], ans;

void Work() {
  inv[1] = f[0] = Inv[0] = 1;
  for(int i = 1; i <= 200000; ++i) {
    f[i] = 1ll * f[i - 1] * i % MOD;
    inv[i] = (i > 1 ? 1ll * (MOD - MOD / i) * inv[MOD % i] % MOD : 1);
    Inv[i] = 1ll * Inv[i - 1] * inv[i] % MOD;
  }
}

int C(ll a, ll b) {
  return (a < b ? 0 : 1ll * f[a] * Inv[b] % MOD * Inv[a - b] % MOD);
}

void Solve() {
  cin >> n >> m >> k;
  ans = 0;
  for(int i = 1; i <= m; ++i) {
    ans = (ans + (1ll * C(m, i) * C(0ll + k + m - 1 - 1ll * i * n, m - 1) % MOD * (i % 2 ? 1 : -1) + MOD) % MOD) % MOD;
  }
  cout << (C(k + m - 1, m - 1) - ans + MOD) % MOD << "\n";
}

int main() {
  ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
  Work();
  for(cin >> t; t--; Solve()) {
  }
  return 0;
}
posted @ 2024-09-02 22:29  Yaosicheng124  阅读(13)  评论(0编辑  收藏  举报