牛客.二分图染色 (组合数学,思维,递推)
题目:传送门
题意
思路
邓老师精讲
这题有一个巧妙的转换就是,将这种二分图问题,转移到二维矩阵上。
这里我们可以将二分图投射到一个 n * n 的矩阵上;
题目要求给边染色,且任意两条红色边不共享端点,任意两条蓝色边不共享端点;
那在矩阵上,问题可以转化为,在 n * n 的矩阵上放若干红色和蓝色棋子,使得任意行和列不存在颜色相同的棋子。
我们设 F[n] 表示在 n * n 放一种颜色的棋子,使得任意行和列最多只有一个棋子的方案数。
那么 F[n] = C(n, i) * A(n, i) (i = 0, 1, 2 .... n) ;(n行中选 i 行,有C(n, i)种,这 i 行去和 n 列中的 i 列匹配,就有 A(n, i) 种)
那么现在有两种颜色的棋子,那么总的方案数就是 F[n] * F[n];这些方案都满足;任意行和列不存在颜色相同的棋子。
但是,这些方案中,存在两种颜色棋子出现在同个格子的情况,这种情况需要减掉。 考虑用容斥;
那么最终答案就是 (-1)^n * C(n, i) * A(n, i) * F[n - i] * F[n - i] (i = 0, 1, ..... n);
现在还存在一个问题,那就是按照上面的公式 F[n] 的预处理是需要 O(n^2) 的,显然不行,考虑如何 O(n) 递推;
在二维矩阵上,n * n 比 (n - 1) * (n - 1) 多了 2 * n - 1 个格子,那么在这些格子上放一个棋子有 2 * n - 1 种选择,加上不放棋子这种情况,那么总的选择就有 2*n 种,总的就是 2*n*F[n-1] 种方案,需要减去一些冲突的情况。只是多放了一个棋子上去,那么最多只有一个格子会出现冲突。
可能出现冲突的格子就是 (n - 1) * (n - 1) 矩阵上的某个格子,那么冲突的方案就是 (n - 1) * (n - 1) * F[n - 2];
所以 F[n] = 2*n*F[n - 1] - (n - 1)^2 * F[n - 2]
当然,如果你实在想不到怎么递推,那就只能打表找规律了。
#include <bits/stdc++.h> #define LL long long #define ULL unsigned long long #define UI unsigned int #define mem(i, j) memset(i, j, sizeof(i)) #define rep(i, j, k) for(int i = j; i <= k; i++) #define dep(i, j, k) for(int i = k; i >= j; i--) #define pb push_back #define make make_pair #define INF 0x3f3f3f3f #define inf LLONG_MAX #define PI acos(-1) #define fir first #define sec second #define lb(x) ((x) & (-(x))) #define dbg(x) cout<<#x<<" = "<<x<<endl; using namespace std; const int N = 1e7 + 5; const LL mod = 1e9 + 7; LL fac[N], ifac[N], f[N]; LL ksm(LL a, LL b) { LL res = 1LL; while(b) { if(b & 1) res = res * a % mod; a = a * a % mod; b >>= 1; } return res; } LL C(int n, int m) { return fac[n] * ifac[m] % mod * ifac[n - m] % mod; } LL A(int n, int m) { return fac[n] * ifac[n - m] % mod; } int n; void solve() { scanf("%d", &n); fac[0] = ifac[0] = 1LL; rep(i, 1, n) fac[i] = 1LL * i * fac[i - 1] % mod; ifac[n] = ksm(fac[n], mod - 2); dep(i, 1, n - 1) ifac[i] = 1LL * (i + 1) * ifac[i + 1] % mod; f[0] = 1LL; f[1] = 2LL; rep(i, 2, n) f[i] = (2LL * i * f[i - 1] % mod - 1LL * (i - 1LL) * (i - 1LL) % mod * f[i - 2] % mod + mod) % mod; LL ans = 0LL; rep(i, 0, n) { if(i & 1) { ans = (ans - C(n, i) * A(n, i) % mod * f[n - i] % mod * f[n - i] % mod + mod) % mod; } else ans = (ans + C(n, i) * A(n, i) % mod * f[n - i] % mod * f[n - i] % mod) % mod; } printf("%lld\n", ans); } int main() { // int _; scanf("%d", &_); // while(_--) solve(); solve(); return 0; }