[USACO20DEC] Sleeping Cows P
阿新 • • 發佈:2021-07-09
分析
將牛和牛棚從小到大排序。
對於存在沒有牛棚的牛的合法方案
考慮列舉最小的沒有牛棚的牛 \(i\) ,以及找到最小可以容納牛 \(i\) 的牛棚 \(j\) 。
容易得到如下性質:
- 對於 牛\(1\) ~ 牛\(i\) ,必定都擁有對應牛棚;
- 對於 牛棚\(j\) ~ 牛棚\(n\) ,必定都擁有對應牛。
可以預處理陣列 \(F_{i,j}\) 與 \(G_{i,j}\) ,其中 \(F_{i,j}\) 表示 牛\(i\) ~ 牛\(n\) 匹配 \(j\) 個牛棚的方案數, \(G_{i,j}\) 表示 牛棚\(1\) ~ 牛棚\(i\) 匹配 \(j\) 個牛的方案數。
列舉 牛\(i\) 的時候,列舉 牛\(1\) ~ 牛\(i-1\) 中有 \(k\) 頭牛與 牛棚\(j\) ~ 牛棚\(n\) 匹配。
因為顯然 牛\(i+1\) ~ 牛\(n\) 只會與 牛棚\(j\) ~ 牛棚\(n\) 匹配 ,牛棚\(1\) ~ 牛棚\(j-1\) 只會與 牛\(1\) ~ 牛\(i-1\) 匹配
而這 \(k\) 頭牛在空出來的地方是可以隨便排的,故還要乘上 \(k!\) 。
那麼對於 牛\(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; }