Sleeping Cows P
题目链接:luogu P7154
题目大意
给你 n 个人 n 个位置,人有大小,位置有大小,人可以进去大小大于等于他的位置。
然后问你有多少个极大匹配,极大匹配就是不能再有没有进入的人进去一个没有人进去的位置。
思路
首先把人和位置从小到大排序。
你首先发现一个人能匹配的是一个后缀,一个房能匹配的是一个前缀。
然后你考虑一个人不匹配会发生什么,就是要它能匹配的房子都要匹配了。
那你考虑把两个数组合并在一起排序,然后 DP。
这样的话,如果你当前是人,你不选了,那后面 DP 到的房子都要选了。
那不难想到一个 DP 方式,设
f
i
,
j
,
k
f_{i,j,k}
fi,j,k 为到
i
i
i 个数,还有
j
j
j 个人等待匹配,然后
k
=
0
/
1
k=0/1
k=0/1 表示已经有人不匹配了或全都还在匹配。
然后考虑当前是人或是房要如何匹配。
先看是人:
f
i
,
j
,
0
=
f
i
−
1
,
j
,
0
+
f
i
−
1
,
j
−
1
,
0
,
f
i
−
1
,
j
,
1
f_{i,j,0}=f_{i-1,j,0}+f_{i-1,j-1,0},f_{i-1,j,1}
fi,j,0=fi−1,j,0+fi−1,j−1,0,fi−1,j,1(第一个是原本已经有没有匹配的这个又不匹配,第二个就是原本有不匹配的这个匹配,第三个是原本都是匹配的,这个不匹配了,就有不匹配了)
f
i
,
j
,
1
=
f
i
−
1
,
j
−
1
,
1
f_{i,j,1}=f_{i-1,j-1,1}
fi,j,1=fi−1,j−1,1(就是一定要匹配)
然后是房:
f
i
,
j
,
1
=
f
i
−
1
,
j
,
1
+
f
i
−
1
,
j
+
1
,
1
∗
(
j
+
1
)
f_{i,j,1}=f_{i-1,j,1}+f_{i-1,j+1,1}*(j+1)
fi,j,1=fi−1,j,1+fi−1,j+1,1∗(j+1)(第一个是不匹配,第二个是匹配,在没有匹配的
j
+
1
j+1
j+1 个里面选一个,下同)
f
i
,
j
,
0
=
f
i
−
1
,
j
+
1
,
0
∗
(
j
+
1
)
f_{i,j,0}=f_{i-1,j+1,0}*(j+1)
fi,j,0=fi−1,j+1,0∗(j+1)(因为你前面有人不匹配了,所以这个一定要匹配)
然后初始化的时候肯定没有人不匹配,就是
f
0
,
0
,
1
=
1
f_{0,0,1}=1
f0,0,1=1。
然后结尾的时候可以一直所有人都匹配,也可以中途有人不匹配,但要匹配的都要全部匹配,就是
f
2
n
,
0
,
0
+
f
2
n
,
0
,
1
f_{2n,0,0}+f_{2n,0,1}
f2n,0,0+f2n,0,1。
然后至于空间有点小危,你可以搞个滚动数组。
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
#define mo 1000000007
using namespace std;
struct node {
int x, y;
}tmp[6001];
int n, s[3001], t[3001];
ll f[2][3001][2], ans;
bool cmp(node x, node y) {
if (x.x != y.x) return x.x < y.x;
return x.y < y.y;
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%d", &s[i]), tmp[i].x = s[i], tmp[i].y = 0;
for (int i = 1; i <= n; i++) scanf("%d", &t[i]), tmp[i + n].x = t[i], tmp[i + n].y = 1;
sort(s + 1, s + n + 1); sort(t + 1, t + n + 1); sort(tmp + 1, tmp + n + n + 1, cmp);
f[0][0][1] = 1;//一开始没有断
for (int i = 1; i <= 2 * n; i++) {
if (tmp[i].y == 0) {
for (int j = 0; j <= n; j++) {
f[i & 1][j][1] = (j ? f[(i & 1) ^ 1][j - 1][1] : 0);//拿去匹配
f[i & 1][j][0] = (f[(i & 1) ^ 1][j][0] + (j ? f[(i & 1) ^ 1][j - 1][0] : 0) + f[(i & 1) ^ 1][j][1]) % mo;//可能是新断了,也可能是原本就断了
}
}
else {
for (int j = 0; j <= n; j++) {
f[i & 1][j][0] = f[(i & 1) ^ 1][j + 1][0] * (j + 1) % mo;//匹配掉了(一定要匹配)
f[i & 1][j][1] = (f[(i & 1) ^ 1][j][1] + f[(i & 1) ^ 1][j + 1][1] * (j + 1) % mo) % mo;//可以匹配,也可以不匹配
}
}
}
ans = (f[0][0][0] + f[0][0][1]) % mo;//爱断不断,*(就可以一直都不断的,全部匹配)
printf("%lld", ans);
return 0;
}