[关闭]
@rebirth1120 2019-08-06T17:03:58.000000Z 字数 1603 阅读 618

[HAOI2001] problem c 解题报告

解题报告 各省省选


题目链接

题目大意

为 n 个人编排位置,不同的人的位置编号可以相同。n 个人会依次去找自己的位置,设第 个人的位置编号为 ,若 已经有人坐了,则这个人会去找 后的位置,找到一个空的位置马上坐下来,若有人无法坐下来,则这个方案是不合法的。
现有 个人,他们的位置已经确定了,求编排位置的方案数。
若没有合法的方案,则输出 NO,
否则输出 YES + 合法方案数。

思路

对于判断是否有合法的方案数,可以先计算出每个位置已经安排的人数的后缀和 ,若 ,则不可能有合法的方案。

对于统计方案数,考虑dp。
为(从后往前)考虑到第 个位置,已经为 个人安排位置的方案数,转移方程为:


为一个组合数,表示当前选 个人的方案数,现在我们就需要考虑 的取值。
若没有预先安排好位置的人(即 m 等于 0)时, 显然取 ,但是由于已经有人预先安排好位置了,所以有一些方案是不能选的。
表示预先安排好位置的人中,把位置安排在 的人数,那么位置 实际上需要安排的人数就是 ,可安排在位置 的人数也不是 ,而是
为什么是减去 ,而不是减去 呢,因为在考虑到位置 时,总共是有 个人还没安排位置,而这 个人中已经预先安排好位置的人数是 ,而不是 ,所以应当减去
所以最终的转移方程为

(注:这里的 表示的是后缀和。)

代码

  1. #include<iostream>
  2. #include<cstring>
  3. #include<cstdio>
  4. #define ll long long
  5. using namespace std;
  6. const int N=300+7;
  7. int T,n,m,bur[N],lim[N];
  8. ll mod,f[N][N],c[N][N];
  9. void init(){
  10. c[0][0]=c[1][0]=c[1][1]=1;
  11. for(int i=2;i<=n;i++)
  12. for(int j=0;j<=i;j++)
  13. c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod;
  14. }
  15. int main(){
  16. //freopen("pc.in","r",stdin);
  17. cin>>T;
  18. while(T--){
  19. memset(f,0,sizeof(f));
  20. memset(bur,0,sizeof(bur));
  21. memset(c,0,sizeof(c));
  22. scanf("%d%d%lld",&n,&m,&mod);
  23. init();
  24. int x,q;
  25. for(int i=1;i<=m;i++){
  26. scanf("%d%d",&x,&q);
  27. bur[q]++;
  28. }
  29. int res=0; bool flag=0;
  30. for(int i=n;i>=1;i--){
  31. res+=bur[i];
  32. if(res>n-i+1){
  33. printf("NO\n");
  34. flag=1;break;
  35. }
  36. }
  37. if(flag) continue;
  38. f[n+1][0]=1;
  39. for(int i=n;i>=1;i--){
  40. lim[i]=n-i+1;
  41. for(int j=bur[i+1]+bur[i];j<=lim[i];j++)
  42. for(int k=bur[i+1];k<=min(j-bur[i],lim[i+1]);k++)
  43. f[i][j]=(f[i][j]+f[i+1][k]*c[n-k-m][j-k-bur[i]]%mod)%mod;
  44. m-=bur[i];
  45. }
  46. printf("YES %lld\n",f[1][n]);
  47. }
  48. return 0;
  49. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注