HAOI2011 Problem c

题目链接:戳我

\(dp[i][j]\)表示在排除掉原先已经有指定座位的人之后,第i个人之后有j个人的编号我们已经人为地确定过了 的方案数。

那么我们就有\(dp[i][j]=dp[i+1][j-k]*C_j^k\)

因为我们需要知道第i个之后有多少个人预先已经确定了座位。所以我们还需要预先处理一个前缀和sum[i],表示第i个之后预先确定好座位的人数。

什么时候不合法呢?当sum[i]>n-i+1的时候一定是不合法了。

另外备注一点,刚开始我写的时候还以为转移方程后面还要乘上一个排列。但是,仔细一想其实不然。如果i个人的位置已经确定,那么方案显然是唯一的!(因为大家都顺序向后坐嘛qwq),所以方案个数的不同,只能不同在我们之后又人为安排的序号上。(唔。。。大家理解到我想表达的是什么意思了嘛qwqwq)

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define MAXN 310
using namespace std;
int t,n,m,M;
int sum[MAXN],c[MAXN][MAXN],dp[MAXN][MAXN];
int main()
{
    #ifndef ONLINE_JUDGE
    freopen("ce.in","r",stdin);
    #endif
    scanf("%d",&t);
    while(t--)
    {
        memset(sum,0,sizeof(sum));
        memset(dp,0,sizeof(dp));
        scanf("%d%d%d",&n,&m,&M);
        for(int i=1;i<=m;i++) 
        {
            int cur,id;
            scanf("%d%d",&cur,&id);
            sum[id]++;
        }
        for(int i=n;i>=1;i--) sum[i]+=sum[i+1];
        bool flag=true;
        for(int i=n;i>=1;i--)
            if(sum[i]>n-i+1)
            {
                printf("NO\n");
                flag=false;
                break;
            }
        if(flag==false) continue;
        /*for(int i=0;i<=10;i++)
        {
            for(int j=0;j<=i;j++)  
                printf("%d ",c[i][j]);
            cout<<endl;
        }*/
        for(int i=0;i<=n;i++) c[i][0]=c[i][i]=1;
        for(int i=2;i<=n;i++)
            for(int j=1;j<i;j++)
                c[i][j]=(c[i-1][j-1]+c[i-1][j])%M;
        dp[n+1][0]=1;
        for(int i=n;i;i--)
            for(int j=0;j<=n-sum[i]-i+1;j++)
                for(int k=0;k<=j;k++)
                    dp[i][j]=(dp[i][j]+1ll*dp[i+1][j-k]*c[j][k]%M)%M;
        printf("YES %d\n",dp[1][n-m]);
    }
    return 0;
}
posted @ 2019-03-03 19:36  风浔凌  阅读(114)  评论(0编辑  收藏  举报