「USACO 2020.12 Platinum」Sleeping Cows

写容斥就输了。。

为每个牛棚考虑牛,从大到小,考虑每一个牛棚是否匹配

$dp_{i,j,f}$表示后$i$个牛棚中有$j$个钦定要匹配但是还未匹配的牛棚,$f=0/1$表示是否存在一个牛棚未选

每次移动$i$,会有一部分牛不能继续匹配

如果$f=1$,那么这部分牛必须被全部后面的$j$个牛棚中某一些匹配,否则不合法

如果$f=0$,这一部分可以随便匹配一定数量的牛

用组合数维护转移权值即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46


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);
}