P7154 [USACO20DEC] Sleeping Cows P 题解
把 \(s, t\) 升序排序。
容易发现每一个 \(t_i\) 可匹配的 \(s_j\) 对应了一个前缀。
考虑刻画极大匹配,一个匹配是极大的当且仅当最大的没有被匹配的 \(t\) 小于最小的没有被匹配的 \(s\)。
证明:充分性显然,如果一个匹配不是极大的,那么 \(\exists i, j, t_i\ge s_j\),其中 \(t_i, s_j\) 未匹配,转化为逆否命题得证必要性。
我们按数值从小到大考虑每一个 \(t_i\) 或 \(s_j\),将它们按升序排成一行,如果把 \(t_i\) 视作黑球,\(s_j\) 视作白球,那么问题转化为了:前面的白球可以选择和后面的某一个黑球匹配,且最小没有匹配的白球后面没有未匹配的黑球。
这个经典的黑白球匹配问题可以用如下 DP 解决:\(f_{i, j, 0/1}\) 表示前 \(i\) 个球,有 \(j\) 个白球需要和后面的黑球匹配,是否有没有匹配的白球,转移考虑当前是黑球白球,以及是否和某个球匹配了。
时间复杂度 \(O(n^2)\)。
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <ctime>
#define int long long
#define x first
#define y second
using namespace std;
typedef pair<int, int> PII;
typedef unsigned long long ull;
const int N = 6000 + 10, mod = 1e9 + 7;int n, a[N], b[N], w[N], f[N][N][2], idx;
PII p[N];signed main() {ios::sync_with_stdio(0), cin.tie(0);cin >> n;for(int i = 1; i <= n; i ++) cin >> a[i], p[++ idx] = {a[i], 0};for(int i = 1; i <= n; i ++) cin >> b[i], p[++ idx] = {b[i], 1};sort(p + 1, p + idx + 1);f[0][0][0] = 1;for(int i = 1; i <= idx; i ++) {for(int j = 0; j <= i; j ++) {for(int o = 0; o < 2; o ++) {if(f[i - 1][j][o]) {int v = f[i - 1][j][o];if(p[i].y) { // blackif(!o) f[i][j][o] = (f[i][j][o] + v) % mod;if(j) f[i][j - 1][o] = (f[i][j - 1][o] + v * j) % mod;}else {f[i][j][1] = (f[i][j][1] + v) % mod;f[i][j + 1][o] = (f[i][j + 1][o] + v) % mod;}}}}}cout << (f[idx][0][0] + f[idx][0][1]) % mod << '\n';return 0;
}
QwQ