[USACO20DEC] Sleeping Cows P
分析
将牛和牛棚从小到大排序。
对于存在没有牛棚的牛的合法方案
考虑枚举最小的没有牛棚的牛 \(i\) ,以及找到最小可以容纳牛 \(i\) 的牛棚 \(j\) 。
容易得到如下性质:
- 对于 牛\(1\) ~ 牛\(i-1\) ,必定都拥有对应牛棚;
- 对于 牛棚\(j\) ~ 牛棚\(n\) ,必定都拥有对应牛。
枚举 牛\(i\) 的时候,枚举 牛\(1\) ~ 牛\(i-1\) 中有 \(k\) 头牛与 牛棚\(j\) ~ 牛棚\(n\) 匹配。
牛棚\(j\) ~ 牛棚\(n\) 中有 \(n-j+1-k\) 个与 牛\(i+1\) ~ 牛\(n\) 匹配。
牛\(1\) ~ 牛\(i-1\) 中有 \(i-1-k\) 个 与 牛棚\(1\) ~ 牛棚\(j-1\) 匹配。
而这 \(k\) 头牛在空出来的地方是可以随便排的,故还要乘上 \(k!\) 。
可以预处理数组 \(F_{i,j}\) 与 \(G_{i,j}\) ,其中 \(F_{i,j}\) 表示 牛\(i\) ~ 牛\(n\) 匹配 \(j\) 个牛棚的方案数, \(G_{i,j}\) 表示 牛棚\(1\) ~ 牛棚\(i\) 匹配 \(j\) 个牛的方案数。
那么对于 牛\(i\) 的答案,即为 \(\sum^{min(i-1,n-j+1)}_{k=0} G[j-1][i-1-k] \times F[i+1][n-j+1-k] \times k!\)
对于所有牛都有与之对应牛棚的合法方案
答案即为 \(F_{1,n}\) 。
代码
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
#define M 3005
const int P=1e9+7;
using namespace std;
typedef long long ll;
typedef double db;
char IO;
int rd(){
int num=0;bool f=0;
while(IO=getchar(),IO<48||IO>57)if(IO=='-')f=1;
do num=(num<<1)+(num<<3)+(IO^48);
while(IO=getchar(),IO>=48&&IO<=57);
return f?-num:num;
}
int n;
int S[M],T[M],cnt[M];
ll F[M][M],G[M][M],Fac[M];
int main(){
n=rd();
Fac[0]=1;for(int i=1;i<=n;++i)Fac[i]=Fac[i-1]*i%P;
for(int i=1;i<=n;++i)S[i]=rd();
for(int i=1;i<=n;++i)T[i]=rd();
sort(S+1,S+n+1);sort(T+1,T+n+1);
for(int i=1;i<=n;++i)
cnt[i]=n-(lower_bound(T+1,T+n+1,S[i])-T)+1;
F[n+1][0]=1;// i-n 牛 匹配 j 个牛棚
for(int i=n;i;--i){
for(int j=cnt[i];~j;--j){
if(j)(F[i][j]+=F[i+1][j-1]*(cnt[i]-j+1))%=P;
(F[i][j]+=F[i+1][j])%=P;
}
}
G[0][0]=1;// 1-i 牛棚 匹配 j 个牛
for(int i=1,lim;i<=n;++i){
lim=upper_bound(S+1,S+n+1,T[i])-S-1;
for(int j=0;j<=lim;++j){
if(j)(G[i][j]+=G[i-1][j-1]*(lim-j+1))%=P;
(G[i][j]+=G[i-1][j])%=P;
}
}
ll ans=F[1][n];
for(int i=1,lim;i<=n;++i){
lim=lower_bound(T+1,T+n+1,S[i])-T;
for(int j=0;j<=min(i-1,n-lim+1);++j)
(ans+=G[lim-1][i-1-j]*F[i+1][n-lim+1-j]%P*Fac[j])%=P;
}
cout<<ans;
return 0;
}

dp
浙公网安备 33010602011771号