Loading

[2018 ICPC] Count the Bits

前言

写的很屎, 看下代码即可, 不用管我说了什么

思路

考虑二进制下类似数位 \(\rm{dp}\) 的做法, 这样稍微复杂度正确一点

相当于记录当前的位数, 之前的 \(1\) 的个数, 对应十进制的值对 \(k\) 取模的值
如何 记忆化 / 剪枝 , 如果我们从高位到低位放, 当前高位对应是背倍数, \(\cdots\)

考虑设计状态 \(f_{p, i, m}\) 表示当前的位数, \(1\) 的个数, 当前的余数 出现了多少次

考虑转移, 从低位到高位考虑

\[\begin{align*} f_{p, i, m} \gets \begin{cases} f_{p - 1, i, m} \\ f_{p - 1, i - 1, (m - 2^p \textrm{ mod } k + k) \textrm{ mod } k} \end{cases} \end{align*}\]

实现中应当使用刷表法更简便
这个看代码吧, 不想写了

数据检验, 懒得检验, 应当是对的

注意最终答案应当在 \(f_{p}\) 中, 因为答案会重复, 没有处理数位的前缀 \(0\) 一类的

实现

#include <iostream>

const int MOD = 1e9 + 9;
const int MAXN = 129;
const int MAXVAL = 1001;

#define int long long

namespace calc {
    int add(int a, int b) { return a + b > MOD ? a + b - MOD : a + b; }
    int mul(int a, int b) { return (a % MOD * b * 1ll) % MOD; }
    int sub(int a, int b) { return a - b < 0 ? a - b + MOD : a - b; }
    void mulon(int &a, int b) { a = mul(a, b); }
    void addon(int &a, int b) { a = add(a, b); }
} using namespace calc;

int k, b;
int f[MAXN][MAXN][MAXVAL];
int pow2[MAXVAL];

int ans = 0;

signed main()
{
    scanf("%lld %lld", &k, &b), --b;
    pow2[0] = 1; for (int i = 1; i <= 1000; i++) pow2[i] = (pow2[i - 1] * 2) % k;
    /*初始化*/
    f[0][1][1 % k] = 1, f[0][0][0] = 1;

    /*转移*/
    for (int p = 0; p < b; p++) 
        for (int i = 0; i <= p + 1; i++) 
            for (int m = 0; m < k; m++) {
                addon(f[p + 1][i][m], f[p][i][m]);
                addon(f[p + 1][i + 1][(m + pow2[p + 1]) % k], f[p][i][m]);
            }
    
    // for (int p = 0; p <= b; p++) 
    for (int i = 0; i <= b + 1; i++) addon(ans, mul(i, f[b][i][0]));

    printf("%lld", ans);

    return 0;
}

总结

注意答案的重复

数位 \(\rm{dp}\) , 注意前导零

posted @ 2025-02-09 14:43  Yorg  阅读(12)  评论(0)    收藏  举报