- 有(n)个值域为([1,n])的数,其中(m)个数已知。
- 求剩余的数有多少种填法,满足(forall i),大于等于(i)的数不超过(n-i+1)个。
- 数据组数(le10,nle300)
(DP)水题
显然,设(f_{i,j})表示大于等于(i)的数有(j)个的方案数。
转移时枚举填了几个(i),系数乘个组合数即可转移。
代码:(O(Tn^3))
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 300
using namespace std;
int n,m,X,a[N+5],c[N+5],s[N+5],f[N+5][N+5],C[N+5][N+5];
int main()
{
RI Tt,i,j,k,x,y;scanf("%d",&Tt);W(Tt--)
{
memset(c,0,sizeof(c)),memset(s,0,sizeof(s)),memset(f,0,sizeof(f));//清空
for(scanf("%d%d%d",&n,&m,&X),i=1;i<=m;++i) scanf("%d%d",&x,&y),++c[y];//c统计每种数给定的个数
for(C[0][0]=i=1;i<=n;++i) for(C[i][0]=j=1;j<=n;++j) C[i][j]=(C[i-1][j-1]+C[i-1][j])%X;//预处理组合数
for(f[n+1][0]=1,i=n;i;--i) for(s[i]=s[i+1]+c[i],j=s[i+1];j<=n-i;++j)//s统计大于等于i的数给定的个数
for(k=c[i];k<=n-j-(m-s[i]);++k) f[i][j+k]=(1LL*f[i+1][j]*C[n-j-(m-s[i+1])][k-c[i]]+f[i][j+k])%X;//枚举有几个i,乘上组合数转移
for(i=1;i<=n;++i) if(s[i]>n-i+1) {puts("NO");break;}i>n&&printf("YES %d
",f[1][n]);//先判无解,再输出答案(不能直接判答案是否为0)
}return 0;
}