题目链接:戳我
设\(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;
}