牛客.二分图染色 (组合数学,思维,递推)

题目:传送门

题意

 

 思路

邓老师精讲

这题有一个巧妙的转换就是,将这种二分图问题,转移到二维矩阵上。

这里我们可以将二分图投射到一个 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;
}

 

posted on 2020-09-18 13:16  Willems  阅读(171)  评论(0编辑  收藏  举报

导航