• 【计数DP】CF1794D


    Problem - D - Codeforces

    题意

    思路

    解法大方向对了,但是还是不会做,原因是组合数不知道怎么求

    首先需要注意到一些东西:

    1.底数一定是质数

    2.质数个数 < n 一定无解

    3.哪些质数作为底数是不确定的

    4.n <= 2022

    那么我们其实可以把做法大致猜出来

    根据第4点,应该是个二维的dp,且状态的设计感觉非常唯一:

    设 dp[i][j] 表示前 i 个数,已经选了 j 个数作为底数的方案数

    然后就是考虑转移,这种计数类的dp在转移的时候都是考虑多一格会多出多少贡献,贡献一般由组合数求出

    问题就是出在这里,我不知道怎么求组合数,一心想着对于指数求可重集排列,但是这么多的指数的个数是很难维护的

    其实应该考虑分配,算出组合就行了,不用去考虑排列

    设多出来那一格的数为 x, 它的个数为 y

    那就是考虑把这 y 个 x 分配到 指数中,指数中还剩余多少个空位呢

    前缀已经选了 j 个底数,设前缀的个数和为 sum,那么前缀有 sum - j 个数作为指数

    所以还剩下 n - (sum - j) 个位置,也就是说,在 n - (sum - j) 个位置中选 y 个位置,那就是 C(n - (sum - j), y)

    如果作为底数也是同样的道理

    对 x 作为指数还是底数讨论一下即可

    Code:

    1. #include
    2. #define int long long
    3. constexpr int N = 1e6 + 10;
    4. constexpr int mod = 998244353;
    5. constexpr int Inf = 0x3f3f3f3f;
    6. int n, x;
    7. int len = 0;
    8. int Fac[N];
    9. int inv[N];
    10. int vis[N], prime[N];
    11. int qpow(int a, int b) {
    12. int res = 1;
    13. while(b) {
    14. if (b & 1) res = (res * a) % mod;
    15. a = (a * a) % mod;
    16. b >>= 1;
    17. }
    18. return res;
    19. }
    20. int C(int n, int m) {
    21. if (n < 0 || m < 0 || n < m) return 0;
    22. return Fac[n] * inv[m] % mod * inv[n - m] % mod;
    23. }
    24. void Fac_init() {
    25. Fac[0] = 1;
    26. for (int i = 1; i < N; i ++) {
    27. Fac[i] = (Fac[i - 1] * i) % mod;
    28. }
    29. inv[N - 1] = qpow(Fac[N - 1], mod - 2);
    30. for (int i = N - 2; i >= 0; i --) {
    31. inv[i] = inv[i + 1] * (i + 1) % mod;
    32. }
    33. }
    34. void P_init(int n) {
    35. vis[1] = 1;
    36. for (int i = 2; i <= n; i ++) {
    37. if (!vis[i]) prime[++len] = i;
    38. for (int j = 1; i <= n / prime[j]; j ++) {
    39. vis[i * prime[j]] = 1;
    40. if (i % prime[j] == 0) break;
    41. }
    42. }
    43. }
    44. void solve() {
    45. std::cin >> n;
    46. std::map<int, int> mp;
    47. for (int i = 1; i <= 2 * n; i ++) {
    48. std::cin >> x;
    49. mp[x] += 1;
    50. }
    51. int sum = 0;
    52. std::vector<int> dp(n + 1, 0);
    53. dp[0] = 1;
    54. for (auto [x, y] : mp) {
    55. std::vector<int> ndp(n + 1, 0);
    56. for (int j = 0; j <= n; j ++) {
    57. ndp[j] += dp[j] * C(n - (sum - j), y) % mod;
    58. ndp[j] %= mod;
    59. if (j >= 1 && !vis[x]) {
    60. ndp[j] += dp[j - 1] * C(n - (sum - j + 1), y - 1) % mod;
    61. ndp[j] %= mod;
    62. }
    63. }
    64. dp.swap(ndp);
    65. sum += y;
    66. sum %= mod;
    67. }
    68. std::cout << dp[n] % mod << "\n";
    69. }
    70. signed main() {
    71. std::ios::sync_with_stdio(false);
    72. std::cin.tie(nullptr);
    73. int t = 1;
    74. Fac_init();
    75. P_init(1e6);
    76. while (t--) {
    77. solve();
    78. }
    79. return 0;
    80. }

  • 相关阅读:
    网络编程/计算机网络
    【华为OD机试真题 JS】玩牌高手
    Octave安装
    windows模拟触摸
    【附源码】计算机毕业设计SSM图书出版系统
    Welcome to YARP - 5.身份验证和授权
    tkinter绘制组件(32)——圆角按钮
    个人和企业如何做跨境电商?用API电商接口教你选平台选品决胜跨境电商
    SpringBoot+Vue项目电子招投标系统
    【Vue.js】使用Element搭建登入注册界面&axios中GET请求与POST请求&跨域问题
  • 原文地址:https://blog.csdn.net/weixin_62528401/article/details/134025364