HNOI 集合选数

题目大意

HNOI 集合选数 - 图1 以内的自然数里面选,对于任意一个自然数HNOI 集合选数 - 图2 选中的数中不能有HNOI 集合选数 - 图3 ,计算出总的方案数

题解

考虑建图,在一个矩阵上 从左到右 依次为前一个数的两倍 ,从上至下依次是前一个数的三倍

题意转化为在这样一个矩阵上, 选择不相邻的数, 因为长和宽都是HNOI 集合选数 - 图4 级别的所有我们考虑状压DP

HNOI 集合选数 - 图5 表示第HNOI 集合选数 - 图6 行,合法状态HNOI 集合选数 - 图7 时 累计的方案数

HNOI 集合选数 - 图8

HNOI 集合选数 - 图9 满足自身和下一行均合法 即使HNOI 集合选数 - 图10 是一个和HNOI 集合选数 - 图11 行冲突的一个合法状态 ,但是这样的话方案数也为HNOI 集合选数 - 图12 对答案无影响

  1. #include <cstdio>
  2. #include <cstring>
  3. #include <algorithm>
  4. typedef long long LL;
  5. static const int maxm = 1e5 + 10;
  6. static const int maxn = 20 + 10;
  7. static const int MOD = 1000000001;
  8. int f[maxn][maxm],lmt[maxm],mtx[maxn][maxn],vis[maxm],bin[maxm];
  9. int n;
  10. LL ans = 1;
  11. int dp(int x){
  12. for(int i = 1;i <= 18;i++)lmt[i] = 0;
  13. mtx[1][1] = x;
  14. for(int i = 2 ;i <= 18;i++)
  15. if(mtx[i - 1][1] * 2 <= n)mtx[i][1] = mtx[i - 1][1] * 2;
  16. else mtx[i][1] = n + 1;
  17. for(int i = 1 ;i <= 18 ;i++)
  18. for(int j = 2;j <= 11 ;j++)
  19. if(mtx[i][j - 1] * 3 <= n)mtx[i][j] = mtx[i][j - 1] * 3;
  20. else mtx[i][j] = n + 1;
  21. for(int i = 1;i <= 18;i++)
  22. for(int j = 1;j <= 11;j++)
  23. if(mtx[i][j] <= n) lmt[i] += bin[j-1] , vis[mtx[i][j]] = 1;
  24. /*
  25. bin是用来占位的 考察这一行最多可以塞几个
  26. */
  27. for(int i = 0;i <= 18;i++)
  28. for(int j = 0;j <= lmt[i];j++)
  29. f[i][j] = 0;
  30. f[0][0] = 1;
  31. for(int i = 0;i <= 18;i++)
  32. for(int j = 0;j <= lmt[i];j++)
  33. if(f[i][j])
  34. for(int k = 0;k <= lmt[i + 1];k++)
  35. if(!(j & k) && (!(k & (k << 1)))) // 自身合法且与下一行合法
  36. f[i + 1][k] = (f[i + 1][k] + f[i][j]) % MOD;
  37. return f[18][0];
  38. }
  39. int main(){
  40. scanf("%d",&n);
  41. bin[0] = 1;
  42. for(int i = 1;i <= n;i++)bin[i] = bin[i-1] << 1;
  43. for(int i = 1;i <= n;i++)
  44. if(!vis[i]) ans = (ans * dp(i)) % MOD;
  45. printf("%lld\n",ans);
  46. return 0;
  47. }