先考虑如何判断无解,设 $sum[i]$ 表示确定的人中,编号大于 $i$ 的人的人数
如果 $sum[i]>n-i+1$ 则无解,进一步考虑设 $f[i][j]$ 表示当前确定完编号大于等于 $i$ 的人,除去原本固定的人还有 $j$ 人已经确定
那么有 $f[i][j]=sum_{k=0}^{j}f[i+1][j-k] cdot C_{j}^{k},j in [0,n-i+1-sum[i]]$
表示在确定 $j-k$ 人的编号的情况下,再选 $k$ 个人编号为 $i$,乘上组合数是因为每个人都是不同的,我们可以在 $j$ 个人中任意选择 $k$ 个编号为 $i$
记得组合数每次都要重新算,因为模数不同...
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> using namespace std; typedef long long ll; inline int read() { int x=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*f; } const int N=507; int T,n,m,mo,sum[N]; ll C[N][N],f[N][N]; inline ll fk(ll x) { return x>=mo ? x-mo : x; } int main() { T=read(); while(T--) { memset(f,0,sizeof(f)); int a,b,flag=1; memset(sum,0,sizeof(sum)); n=read(),m=read(),mo=read(); for(int i=1;i<=m;i++) { a=read(),b=read(); sum[b]++; } for(int i=n;i;i--) { sum[i]+=sum[i+1]; if(sum[i]>n-i+1) { flag=0; break; } } if(!flag) { printf("NO "); continue; } // f[i][j]+=f[i+1][j-k]*C[j][k] for(int i=0;i<=300;i++) { C[i][0]=1; for(int j=1;j<=i;j++) C[i][j]=fk(C[i-1][j]+C[i-1][j-1]); } f[n+1][0]=1; for(int i=n;i>=1;i--) for(int j=0;j<=n-i+1-sum[i];j++) for(int k=0;k<=j;k++) f[i][j]=fk(f[i][j]+f[i+1][j-k]*C[j][k]%mo); printf("YES %lld ",f[1][n-m]); } return 0; }