1. 程式人生 > 實用技巧 >P2523 [HAOI2011]Problem c DP+組合

P2523 [HAOI2011]Problem c DP+組合

題意:

給 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方程。

  1. \(s[i]\)表示編號大於等於\(i\)的人的個數,對於有解的情況必定滿足對於任意的\(1\leq i \leq n, s[i]\leq (n-i+1)\)因為編號為\(i\)以後的凳子只有\(n-i+1\)

  2. \(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;
    }