SYSU-10,URAL 1675,容斥原理
我们队没人会做tm数学,心塞
题目大意:对于一个n*m的01矩阵,问有多少种可能是恰好k行l列全为1,其他行列不全为一。
解:首先我们可以枚举哪些行列全唯一,然后把剩余的行列拿出来,等价于求n,m,0,0。然后我们做以下考虑,假设我们已经保证了所有行都不为0,那么每种情况都会被归类于他们恰有i列全为1。所以我们设f[i]为所有全1列数大于i的方案,所以答案等于 total - f[1], total其实就是C(m,0)* (2^m-1)^n。
递归考虑f[1],f[1] = total2 - f[2]。如此递归直到f[m] = 0。把式子结合起来,就是:
ans = C(m,0)* (2^m-1)^n - C(m,1)* (2^(m-1)-1)^n + C(m,2)* (2^(m-2)-1)^n ............
(我是无法理解为何是容斥原理,形式很像,各种直觉上来说也是,但是我没办法把他化成集合与的加加减减这种形式)
1 #include <cstdio> 2 #include <iostream> 3 #include <cstring> 4 #include <algorithm> 5 #include <cmath> 6 7 using namespace std; 8 9 #define MAXN 111111 10 #define LL long long 11 12 const LL MOD = 1e9 + 7; 13 14 LL poww[MAXN], inv[MAXN]; 15 16 int n, m, k, l; 17 18 LL quick_pow(LL a, LL b) { 19 LL res = 1, base = a; 20 while (b > 0) { 21 if (b&1) res = (res * base) % MOD; 22 base = (base * base ) % MOD; 23 b >>= 1; 24 } 25 return res; 26 } 27 28 void pre() { 29 poww[0] = 1; 30 inv[0] = 1; 31 for (int i = 1; i < MAXN; ++i) { 32 poww[i] = poww[i-1] * i % MOD; 33 inv[i] = quick_pow(poww[i], MOD - 2); 34 } 35 } 36 37 #define C(n,m) ((m)>(n)?0:(poww[n]*inv[m]%MOD)*inv[n-m]%MOD) 38 39 LL calc(int n, int m) { 40 LL res = 0; 41 for (int i = m, k = 1; i >= 0; --i, k = -k) { 42 res += (MOD + k * (C(m, i) * quick_pow(quick_pow(2, i) - 1, n))); 43 res %= MOD; 44 } 45 return res; 46 } 47 48 int main() { 49 pre(); 50 while (scanf("%d%d%d%d", &n, &m, &k, &l) == 4) { 51 LL ans = (C(n, k) * C(m, l) % MOD * calc(n - k, m - l) % MOD) + MOD; 52 cout << ans % MOD << endl; 53 } 54 return 0; 55 }