题目概述
有 \(n\times m\) 的棋盘,现在需要涂 \(k\) 种颜色上去,需要满足:
- 每一行至少有一个格子被涂色。
- 每一列至少有一个格子被涂色。
- \(k\) 种颜色必须都在这个棋盘上出现。
数据范围:\(1\leq n,m,k\leq 400\)。
分析
经典题目,记录一下。
多重容斥的经典题目。
我们考虑枚举 \(i,j,t\) 表示至少有 \(i\) 行格子被涂色,至少有 \(k\) 行格子被涂色,至少有 \(k\) 种颜色涂了。
所以说:
\[ans=\sum_{i=0}^n\sum_{j=0}^m\sum_{t=0}^k(-1)^{i+j+k}C_{n}^iC_{m}^jC_{k}^t F(i,j,t)
\]
其中 \(F(i,j,t)\) 表示这种情况下的方案数是多少。
那么对于剩下的行数、列数的格子有 \((n-i)\times (m-j)\) 个。
每个格子有两种选择:
- 不被涂色。
- 涂成剩下 \((k-t)\) 种颜色下的一种。
也就是说,每个格子有 \((k-t+1)\) 种选择,所以:
\[F(i,j,t)=(k-t+1)^{(n-i)\times (m-j)}
\]
那么就直接这样做就行了。
代码
时间复杂度 \(\mathcal{O}(nmc)\)。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <stdlib.h>
#include <algorithm>
#include <vector>
#define int long long
#define N 405
using namespace std;
const int mod = 1e9 + 7;
int qpow(int a,int b) {int res = 1;while(b) {if (b & 1) res = res * a % mod;a = a * a % mod;b >>= 1;}return res;
}
int jc[N],inv[N];
int C(int a,int b) {if (a < 0 || b < 0 || a < b) return 0;return jc[a] * inv[b] % mod * inv[a - b] % mod;
}
signed main(){jc[0] = jc[1] = inv[0] = inv[1] = 1;for (int i = 2;i < N;i ++) jc[i] = jc[i - 1] * i % mod,inv[i] = (mod - mod / i) * inv[mod % i] % mod;for (int i = 2;i < N;i ++) inv[i] = inv[i - 1] * inv[i] % mod;int ans = 0,n,m,k;cin >> n >> m >> k;for (int i = 0;i <= n;i ++)for (int j = 0;j <= m;j ++)for (int t = 0;t <= k;t ++) {int xs = ((i + j + t & 1) ? -1 : 1);ans = (ans + (xs * C(n,i) * C(m,j) % mod * C(k,t) % mod * qpow((k - t + 1),(n - i) * (m - j)) % mod + mod) % mod) % mod;}cout << ans;return 0;
}