「USACO 2020.12 Platinum」Sleeping Cows
写容斥就输了。。
为每个牛棚考虑牛,从大到小,考虑每一个牛棚是否匹配
\(dp_{i,j,f}\)表示后\(i\)个牛棚中有\(j\)个钦定要匹配但是还未匹配的牛棚,\(f=0/1\)表示是否存在一个牛棚未选
每次移动\(i\),会有一部分牛不能继续匹配
如果\(f=1\),那么这部分牛必须被全部后面的\(j\)个牛棚中某一些匹配,否则不合法
如果\(f=0\),这一部分可以随便匹配一定数量的牛
用组合数维护转移权值即可
const int N=3010,P=1e9+7;
int n;
int dp[N][N][2],I[N],J[N],C[N][N];
int A[N],B[N];
void Add(int &u,int x){
u+=x,Mod1(u);
}
ll qpow(ll x,ll k=P-2) {
ll res=1;
for(;k;k>>=1,x=x*x%P) if(k&1) res=res*x%P;
return res;
}
int main(){
freopen("cow.in","r",stdin),freopen("cow.out","w",stdout);
n=rd();
rep(i,1,n) B[i]=rd();
rep(i,1,n) A[i]=rd();
sort(A+1,A+n+1),sort(B+1,B+n+1);
int p=n;
dp[n+1][0][0]=1;
rep(i,0,n) rep(j,C[i][0]=1,i) C[i][j]=(C[i-1][j]+C[i-1][j-1])%P;
rep(i,J[0]=1,n) J[i]=1ll*J[i-1]*i%P;
I[n]=qpow(J[n]);
drep(i,n,1) I[i-1]=1ll*I[i]*i%P;
drep(i,n,0) {
int c=0;
while(p && B[p]>A[i]) c++,p--;
rep(j,0,n-i) {
rep(k,0,min(j,c)) {
int t=1ll*dp[i+1][j][0]*C[c][k]%P*C[j][k]%P*J[k]%P;
Add(dp[i][j-k][1],t),Add(dp[i][j-k+1][0],t);
if(k==c) {
t=1ll*dp[i+1][j][1]*C[j][k]%P*J[k]%P;
Add(dp[i][j-k][1],t),Add(dp[i][j-k+1][1],t);
}
}
}
}
int ans=(dp[0][0][0]+dp[0][0][1])%P;
printf("%d\n",ans);
}