题解 P7154 【[USACO20DEC] Sleeping Cows P】

· · 题解

看到题解里没有跟我做法一样的,就来写一篇(

首先很容易想到把 s,t 分别从小到大排序,不影响答案。

然后可以暴力统计出 s 中每个数可以跟 t 中多少数匹配(记为 a_i)。以及 t 中每个数可以跟 s 中多少数匹配(记为 b_i)。

接下来我们考虑枚举一个 i,并计算出所有未匹配的牛中编号最小值为 i 时的方案数。

设这个 i 可以匹配到的牛棚中编号最小值为 t

此时我们发现,编号在 [t,n] 中的牛棚必须被其它的牛匹配完毕,否则这个匹配就不是极大的。

现在有这样几种匹配方式:

显然 [1,i) 中的每一头牛都可以与 [t,n] 中的任何一个牛棚匹配。而 [1,i) 中不与 [t,n] 中某一个牛棚匹配的牛一定全部都与 [1,t) 中某一个牛棚匹配。

我们就可以考虑枚举一个 j,并算出 [1,i) 中的牛与 [t,n] 中的牛棚一共匹配了 j 对的方案数。

此时可以发现:

这样就可以利用很久之前算出的 a,b 来预处理。

$g_{i,j}$ 表示 $[i,n]$ 中的**牛**与所有**牛棚**匹配 $j$ 对的方案数。 具体转移比较显然,可以看代码( 然后我们利用计算出的 $f,g$ 来求答案。枚举到 $i,j$ 时的答案就是 $f_{t-1,i-j-1}\times g_{i+1,n-t-j+1}\times j!$。其中 $j!$ 的意思就是 $[1,i)$ 中的牛与 $[t,n]$ 中的牛匹配 $j$ 对的方案数,因为可以任意匹配。 注意答案还要加上 $f_{n,n}$,表示全部牛都匹配完的方案数。 注:代码中变量名与题解中不太一样,$a1=s,a2=t,b1=a,b2=b,dp1=f,dp2=g$。 ```cpp #include <bits/stdc++.h> using namespace std; #define N 3005 #define MOD 1000000007 int n,ans,fc[N],a1[N],a2[N],b1[N],b2[N],dp1[N][N],dp2[N][N]; int main() { scanf("%d",&n);fc[0]=dp1[0][0]=dp2[n+1][0]=1; for(int i=1;i<=n;++i) fc[i]=1ll*fc[i-1]*i%MOD; for(int i=1;i<=n;++i) scanf("%d",&a1[i]);sort(a1+1,a1+n+1); for(int i=1;i<=n;++i) scanf("%d",&a2[i]);sort(a2+1,a2+n+1); for(int i=1;i<=n;++i) for(int j=1;j<=n;++j) if(a1[i]<=a2[j]) ++b1[i],++b2[j]; for(int i=1;i<=n;++i) { dp1[i][0]=1; for(int j=1;j<=i;++j) dp1[i][j]=(dp1[i-1][j]+1ll*dp1[i-1][j-1]*max(b2[i]-j+1,0))%MOD; } for(int i=n;i;--i) { dp2[i][0]=1; for(int j=1;j<=n-i+1;++j) dp2[i][j]=(dp2[i+1][j]+1ll*dp2[i+1][j-1]*max(b1[i]-j+1,0))%MOD; } for(int i=1,t=1;i<=n;++i) { while(t<=n && a1[i]>a2[t]) ++t; for(int j=0;j<=min(i-1,n-t+1);++j) ans=(ans+1ll*dp1[t-1][i-j-1]*dp2[i+1][n-t-j+1]%MOD*fc[j])%MOD; }printf("%d\n",(ans+dp1[n][n])%MOD);return 0; } ```