题解 P7154 【[USACO20DEC] Sleeping Cows P】
动态规划优化
题目链接:P7154 [USACO20DEC] Sleeping Cows P
本题解同步发布于 My Blog
题意:
现给定长度均为
n 的数组s_i 与t_i ,s_i 能与t_j 匹配当且仅当s_i\le t_j 。一组匹配是极大的,当且仅当对于任意一个匹配对(s_i,t_j) 均满足s_i\le t_j ,且任意未被匹配的s 与t 中无法再组成匹配对。求所有极大匹配的方案数。
考虑将
那么考虑从小到大讨论每一个
不难发现,当我们将
若当前
也就是说,当存在一个
同时,我们实际上只关注最小的,被放弃的
考虑最小的被放弃的
考虑在这个思路上进行优化。实际上,我们并不需要枚举
那么可以维护两个指针,分别指向当前还未考虑的最小的
更简单的实现方法,可以将两个数组合并为一个,进行双关键字排序,依次分类讨论更新即可。以下的
考虑
那么可以分类进行转移:
若第
可以考虑是否将这个
若第
若当前第三位为
最后的答案就是:
也就是最后没有在匹配中,却未选择与谁匹配的
时间复杂度
//Code By CXY07
#include<bits/stdc++.h>
using namespace std;
//#define FILE
#define int long long
#define debug(x) cout << #x << " = " << x << endl
#define file(FILENAME) freopen(FILENAME".in", "r", stdin), freopen(FILENAME".out", "w", stdout)
#define LINE() cout << "LINE = " << __LINE__ << endl
#define LL long long
#define ull unsigned long long
#define pii pair<int,int>
#define mp make_pair
#define pb push_back
#define fst first
#define scd second
#define inv(x) qpow((x),mod - 2)
#define lowbit(x) ((x) & (-(x)))
#define abs(x) ((x) < 0 ? (-(x)) : (x))
#define randint(l, r) (rand() % ((r) - (l) + 1) + (l))
#define vec vector
const int MAXN = 6010;
const int INF = 2e9;
const double PI = acos(-1);
const double eps = 1e-6;
const int mod = 1e9 + 7;
//const int mod = 998244353;
//const int G = 3;
//const int base = 131;
int n, m;
int dp[2][MAXN][2]; //dp[i][j][0/1] 还有j头牛需要加入,是否填满
int now, pre = 1;//滚动数组
pii s[MAXN];
template<typename T> inline bool read(T &a) {
a = 0; char c = getchar(); int f = 1;
while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();}
while(c >= '0' && c <= '9') {a = a * 10 + (c ^ 48); c = getchar();}
a *= f;
return 1;
}
template<typename A, typename ...B>
inline bool read(A &x, B &...y) {return read(x) && read(y...);}
signed main () {
#ifdef FILE
freopen(".in","r",stdin);
freopen(".out","w",stdout);
#endif
read(n);
for(int i = 1; i <= n; ++i) read(s[i].fst), s[i].scd = 0;
for(int i = 1; i <= n; ++i) read(s[i + n].fst), s[i + n].scd = 1;
sort(s + 1, s + n * 2 + 1);
dp[now][0][1] = 1;
for(int i = 1; i <= n * 2; ++i) {
swap(now, pre);//滚动数组
memset(dp[now], 0, sizeof dp[now]);
if(!s[i].scd) {//若当前是 s 中元素
for(int j = 0; j <= n; ++j) {
if(j) (dp[now][j][0] += dp[pre][j - 1][0]) %= mod;
if(j) (dp[now][j][1] += dp[pre][j - 1][1]) %= mod;
(dp[now][j][0] += dp[pre][j][0] + dp[pre][j][1]) %= mod;
}
} else {//若当前是 t 中元素
for(int j = 0; j <= n; ++j) {
(dp[now][j][1] += dp[pre][j][1]) %= mod;
(dp[now][j][1] += dp[pre][j + 1][1] * (j + 1) % mod) %= mod;
(dp[now][j][0] += dp[pre][j + 1][0] * (j + 1) % mod) %= mod;
}
}
}
printf("%lld\n", ((dp[now][0][0] + dp[now][0][1]) % mod + mod) % mod);
return 0;
}