【洛谷2523】[HAOI2011] Problem c(DP水题)
- 有\(n\)个值域为\([1,n]\)的数,其中\(m\)个数已知。
- 求剩余的数有多少种填法,满足\(\forall i\),大于等于\(i\)的数不超过\(n-i+1\)个。
- 数据组数\(\le10,n\le300\)
\(DP\)水题
根据霍尔定理,就是求剩余的数有多少种填法,满足\(\forall i\),大于等于\(i\)的数不超过\(n-i+1\)个。
显然,设\(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\n",f[1][n]);//先判无解,再输出答案(不能直接判答案是否为0)
}return 0;
}
待到再迷茫时回头望,所有脚印会发出光芒