[HAOI2011] Problem c
阿新 • • 發佈:2018-12-18
Description
有\(n\)個人\(n\)個座位,需要給每個人確定一個\(1-n\)的編號,編號可以相同。
接著從第一個人開始依次入座,每個人會嘗試坐到\(a_i\),如果\(a_i\)被佔據了,就嘗試\(a_{i+1},a_{i+2}\dots a_n\)。如果嘗試到第\(n\)個還不行,這個安排方案就不合法。同時有\(m\)個人的編號已經確定了,只能安排剩下的人的編號。求合法的安排方案。
Solution
首先考慮什麼是不合法的方案。
設\(sum[i]\)表示最多可以讓多少人編號\(\leq i\)
如果\(sum[i]<i\)那該方案就不合法
正確性挺顯然的,就是入座的時候只可能編號小的人坐到編號大的座位上,不能從大到小坐。如果\(sum[i]<i\)
然後考慮DP解決這個問題。考慮每個位置可以放哪些元素。
沿用剛才的狀態設計思路,設\(f[i][j]\)表示有\(j\)個人編號\(\leq i\)的方案數,那麼\(i\leq j\leq sum[i]\)。
列舉編號恰好為\(i\)的有\(k\)個,那麼\(cnt[i]\leq k\leq j-(i-1)\),其中\(cnt[i]\)表示欽定編號為\(i\)的個數,\(j-(i-1)\)是至少要給前\(i-1\)個位置留\(i-1\)個人來填滿。
那轉移就是\(f[i][j]+=f[i-1][j-k]\times C(sum[i]-cnt[i]-j+k,k-cnt[i])\)
Code
#include<set> #include<map> #include<cmath> #include<queue> #include<cctype> #include<vector> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using std::min; using std::max; using std::swap; using std::vector; const int N=305; typedef double db; typedef long long ll; #define pb(A) push_back(A) #define pii std::pair<int,int> #define all(A) A.begin(),A.end() #define mp(A,B) std::make_pair(A,B) int sum[N],c[N][N]; int f[N][N],ZYZ,cnt[N]; int getint(){ int X=0,w=0;char ch=0; while(!isdigit(ch))w|=ch=='-',ch=getchar(); while( isdigit(ch))X=X*10+ch-48,ch=getchar(); if(w) return -X;return X; } signed main(){ int T=getint(); while(T--){ memset(f,0,sizeof f); memset(c,0,sizeof c); memset(cnt,0,sizeof cnt); memset(sum,0,sizeof sum); int n=getint(),m=getint();ZYZ=getint(); for(int i=1;i<=m;i++) getint(),cnt[getint()]++; sum[0]=n-m;int flag=0; for(int i=1;i<=n;i++) { sum[i]=sum[i-1]+cnt[i]; if(sum[i]<i) { flag=1; printf("NO\n"); break; } } if(flag) continue; c[0][0]=1; for(int i=1;i<=n;i++){ c[i][0]=1; for(int j=1;j<=n;j++) c[i][j]=(c[i-1][j-1]+c[i-1][j])%ZYZ; } f[0][0]=1; for(int i=1;i<=n;i++){ for(int j=i;j<=sum[i];j++){ for(int k=cnt[i];k<=j-i+1;k++) f[i][j]=(1ll*f[i][j]+1ll*f[i-1][j-k]*c[sum[i]-cnt[i]-j+k][k-cnt[i]]%ZYZ)%ZYZ; } } printf("YES %d\n",f[n][n]); } return 0; }