P2523 [HAOI2011]Problem c DP+組合
阿新 • • 發佈:2020-09-11
題意:
給 n 個人安排座位,先給每個人一個 \(1\thicksim n\) 的編號,設第i 個人的編號為 \(a_i\)(不同人的編號可以相同)。
接著從第一個人開始,依次入座,第 i 個人來了以後嘗試坐到\(a_i\),如果 \(a_i\) 被佔據了,就嘗試 \(a_{i+1}\),若\(a_{i+1}\)也被佔據了的話就嘗試\(a_{i+2}\) ……,如果一直嘗試到第 n 個都不行,該安排方案就不合法。
然而有 m個人的編號已經確定,你只能安排剩下的人的編號,求有多少種合法的安排方案模M。
範圍&性質:\(1\leq T\leq 10\),\(1\leq n,m\leq 300,2\leq M\leq 10^9,1\leq p_i,q_i\leq n\)
分析:
分析題意可以得到,對於不同編號的排列,可能得到的座位結果是相同的,所以我們考慮用編號構造DP方程。
-
記\(s[i]\)表示編號大於等於\(i\)的人的個數,對於有解的情況必定滿足對於任意的\(1\leq i \leq n, s[i]\leq (n-i+1)\)因為編號為\(i\)以後的凳子只有\(n-i+1\)個
-
記\(f[i][j]\)表示剩餘\(n-m\)個人中,已經確定了\(j\)個人,編號大於等於\(i\)可以得到轉移方程如下:
$$ f[i][j]=f[i+1][j-k]\times C_{j}^{k} (0\leq k\leq n-i+1-s[i]) $$
整體的時間複雜度為\(\omicron(Tn^3)\)的
程式碼:
#include<bits/stdc++.h> using namespace std; namespace zzc { long long c[305][305],sum[305],f[305][305]; long long t,n,m,mod; void work() { scanf("%lld",&t); while(t--) { bool flag=false; memset(sum,0,sizeof(sum)); memset(f,0,sizeof(f)); scanf("%lld%lld%lld",&n,&m,&mod); for(int i=1;i<=m;i++) { long long x,tmp; scanf("%lld%lld",&x,&tmp); sum[tmp]++; } for(int i=n;i>=1;i--) { sum[i]+=sum[i+1]; if(sum[i]>n-i+1) { printf("NO\n"); flag=true; break; } } if(flag) continue; c[0][0]=1; c[1][1]=c[1][0]=1; for(int i=2;i<=n;i++) { c[i][0]=1; for(int j=1;j<=i;j++) { c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod; } } f[n+1][0]=1; for(int i=n;i>=1;i--) { for(int j=0;j<=n-sum[i]-i+1;j++) { for(int k=0;k<=j;k++) { f[i][j]=(f[i][j]+f[i+1][j-k]*c[j][k]%mod)%mod; } } } printf("YES %lld\n",f[1][n-m]); } } } int main() { zzc::work(); return 0; }