[題解] P7154 [USACO20DEC] Sleeping Cows P
[題解] P7154 [USACO20DEC] Sleeping Cows P
解題報告
有 \(n\) 頭奶牛和 \(n\) 個牛棚,一個奶牛 \(u_i\) 能進入一個牛棚 \(v_i\) 當且僅當 \(s_{u_i}\leq t_{v_i}\),一個牛棚最多隻能容納一頭牛。
定義一個完美匹配為:對於所有未分配的奶牛,都不能有任何一個未匹配的牛棚滿足條件,求完美匹配的方案數。
關鍵是找到那些沒有匹配的牛棚來計算。
考慮把 \(s\) 和 \(t\) 都放到一起排序,這樣做的好處就是:對於一個牛棚,如果前面有不需要匹配的牛,那麼這個牛棚必須匹配一頭牛。
於是設計狀態為 \(f[i,j,0/1]\) 表示當前考慮了前 \(i\) 個元素,有 \(j\) 頭牛準備被分配到牛棚裡但是還沒有分配,\(0/1\) 表示有沒有丟掉牛(不匹配的牛),\(0\) 表示有,\(1\) 表示沒有。
注意:\(j\) 和第三維的 \(0/1\) 沒有任何關係。
初始為 \(f[0][0][1]=1\),因為任何牛都沒有丟掉。
狀態轉移
設考慮到了第 \(i\) 個位置。
- 第 \(i+1\) 位置為牛。
\(f[i,j,0]->f[i+1,j+1,0],f[i+1,j,0]\)
\(f[i,j,1]->f[i+1,j+1,1],f[i+1,j,0]\)
分為下一輪丟沒丟奶牛進行考慮。
- 第 \(i+1\) 個位置為牛棚。
\(j\times f[i,j,0]->f[i+1,j-1,0]\)
\(j\times f[i,j,1]->f[i+1,j-1,1]\)
\(f[i,j,1]->f[i+1,j,1]\)
前兩種情況是考慮把當前需要分配的 \(j\) 頭牛分配進去。
第三種情況是這個牛棚直接作廢。
注意:如果當前丟掉了一些奶牛,那麼這個牛棚必須被填滿,所以 \(f[i,j,0]\) 是無法轉移到 \(f[i+1,j,0]\) 的。
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; template <typename T> inline T read(){ T x=0;char ch=getchar();bool fl=false; while(!isdigit(ch)){if(ch=='-')fl=true;ch=getchar();} while(isdigit(ch)){ x=(x<<3)+(x<<1)+(ch^48);ch=getchar(); } return fl?-x:x; } #define LL long long const int maxn = 3000 + 10; const int P = 1e9 + 7; struct node{ int val,type; bool operator <(const node &x)const{ if(val!=x.val)return val<x.val; return type<x.type; } }a[maxn<<1]; int s[maxn],t[maxn],n; int f[maxn<<1][maxn][2],ans; inline void add(int &a,int b){ a+=b; if(a>=P)a-=P; } #define read() read<int>() int main(){ n=read(); for(int i=1;i<=n;i++)s[i]=read(),a[i]=(node){s[i],0}; for(int i=1;i<=n;i++)t[i]=read(),a[i+n]=(node){t[i],1}; sort(a+1,a+1+n*2); f[0][0][1]=1; for(int i=0;i<n*2;i++){ if(a[i+1].type==0){ for(int j=0;j<=n+1;j++){ add(f[i+1][j+1][0],f[i][j][0]); add(f[i+1][j][0],f[i][j][0]); add(f[i+1][j+1][1],f[i][j][1]); add(f[i+1][j][0],f[i][j][1]); } } else { for(int j=0;j<=n+1;j++){ if(j>0)add(f[i+1][j-1][0],1LL*j*f[i][j][0]%P); if(j>0)add(f[i+1][j-1][1],1LL*j*f[i][j][1]%P); add(f[i+1][j][1],f[i][j][1]); } } } ans=f[n*2][0][0]+f[n*2][0][1];ans%=P; printf("%d\n",ans); return 0; }
小結
核心是放在一起排序DP,保證了合法狀態方便轉移。