[USACO20DEC] Sleeping Cows P

题目传送门

分析

将牛和牛棚从小到大排序。

对于存在没有牛棚的牛的合法方案

考虑枚举最小的没有牛棚的牛 \(i\) ,以及找到最小可以容纳牛 \(i\) 的牛棚 \(j\)

容易得到如下性质:

  1. 对于 牛\(1\) ~ 牛\(i-1\) ,必定都拥有对应牛棚;
  2. 对于 牛棚\(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;
}
posted @ 2021-07-09 20:57  Alnorie  阅读(49)  评论(0)    收藏  举报