题目链接:二分图染色
Description
给定一个完全二分图,图的左右两边顶点数目相同。
每条边我们都要染成红、绿、蓝中的一种。
要求满足任意两条红边不共享端点,任意两条蓝边不共享端点。
求出所有满足条件的染色方案数,答案对取模。
注:表示二分图其中一边的点数目。
数据范围
Solution
我们切换思路,先默认所有的边都是绿色的,这些边的端点没有任何限制。
接下来,我们尝试将部分边给染成红、蓝色。
我们定义 表示二分图其中一边有个点,并且边只有绿色和红色时的答案。
显然,边界条件为 。
我们考虑如何递推这个 序列, 注意到 从 转移过来会新增两个点。
我们分五种情况讨论:
- 1 这两个点出度为,那么方案数是
- 2 这两个点之间连边,那么方案数是
- 3 左点有出度,但不连向右点,那么方案数是
- 4 右点有出度,但不连向左点,那么方案数是
- 5 左点有出度,但不连向右点,右点有出度,但不连向左点,这一部分是3和4的重复部分,需要减掉。方案数是
综上,
于是我们可以在的复杂度内预处理出 序列。
好,那么我们再考虑加入蓝色边,注意到蓝色边和红色边本质相同,因此答案一定是个对称的式子。
你会发现这是一个元的容斥计数,答案为:
反思
这一道题的瓶颈在于如何寻找突破口。
我们先固定了两种颜色去考虑,然后再加入新的颜色,并用容斥的思想去排斥和添加新的值。
同时,我们也要分析和之间对应的所有可能关系,新增两个点的所有可能连边情况,这样才能跨过这个坎。
数列网站(oeis)固然有用,但是我们要学会自己现推的本领,要冷静分析数列间的微妙关系。
另外,做题不取模,爆零两行泪~
做题不开long long,依旧爆零两行泪233~
Code
// Author: wlzhouzhuan #pragma GCC optimize(2) #pragma GCC optimize(3) #include <bits/stdc++.h> using namespace std; #define ll long long #define ull unsigned long long #define rint register int #define rep(i, l, r) for (rint i = l; i <= r; i++) #define per(i, l, r) for (rint i = l; i >= r; i--) #define mset(s, _) memset(s, _, sizeof(s)) #define pb push_back #define pii pair <int, int> #define mp(a, b) make_pair(a, b) inline int read() { int x = 0, neg = 1; char op = getchar(); while (!isdigit(op)) { if (op == '-') neg = -1; op = getchar(); } while (isdigit(op)) { x = 10 * x + op - '0'; op = getchar(); } return neg * x; } inline void print(int x) { if (x < 0) { putchar('-'); x = -x; } if (x >= 10) print(x / 10); putchar(x % 10 + '0'); } const int N = 10000001; const int mod = 1e9 + 7; int fac[N], invf[N]; int qpow(int a, int b) { int ret = 1; while (b > 0) { if (b & 1) ret = 1ll * ret * a % mod; a = 1ll * a * a % mod; b >>= 1; } return ret; } void pre(int n) { fac[0] = invf[0] = 1; for (int i = 1; i <= n; i++) fac[i] = 1ll * fac[i - 1] * i % mod; invf[n] = qpow(fac[n], mod - 2); for (int i = n - 1; i >= 1; i--) invf[i] = 1ll * invf[i + 1] * (i + 1) % mod; } int C(int n, int m) { return n >= m ? 1ll * fac[n] * invf[n - m] % mod * invf[m] % mod : 0; } int f[N], n; int main() { n = read(); pre(n); f[0] = 1, f[1] = 2; for (int i = 2; i <= n; i++) f[i] = (2ll * i * f[i - 1] % mod - 1ll * (i - 1) * (i - 1) % mod * f[i - 2] % mod + mod) % mod; #ifdef debug for (int i = 0; i <= n; i++) { printf("f[%d] = %d\n", i, f[i]); } #endif int ans = 0; for (int i = 0; i <= n; i++) { int opt = (i & 1) ? -1 : 1; int res = (1ll * C(n, i) * C(n, i) % mod * fac[i] % mod * f[n - i] % mod * f[n - i] % mod) % mod; ans = (ans + 1ll * opt * res % mod + mod) % mod; } printf("%d\n", ans); return 0; }