[SDOI2010]代码拍卖会——DP


原题戳这里
绝对是一道好题
需要注意到两个东西
**
1.符合条件的数可以拆成一堆\(11...11\)相加的形式,比如\(1145=1111+11+11+11+1\)
2.\(1,11,111,1111,...\)\(p\)会出现循环,循环节长度不超过\(p\)
**
还有就是\(11...11\)最多为\(9\)个,然后就可以\(dp\)
首先需要统计长度为\(1-n\)的全\(1\)串中有多少个模\(p\)\(r\),记为\(cnt[r]\),这个可以\(O(p)\)的预处理出来
接着设\(f[k][i][j]\)表示在\(cnt[1]-cnt[i]\)中已经选了\(k\)个,且它们的和模\(p\)\(j\)的方案数,那么转移如下:

\[f[k+l][i][(j+l\times i)\% p]+=\binom{cnt[i]+l-1}{l}f[k][i-1][j],0\leqslant k+l\leqslant 8 \]

为什么是\(8\)而不是\(9\)呢?因为我们至少要选一个长度为\(n\)的全\(1\)串,为了方便,我们在\(dp\)之前就把它选出来,相当于占用了\(9\)个中的\(1\)
还有就是那个组合是可重组合
细节看代码吧:

#include 

using namespace std;

#define $SHOW(x) cout << #x" = " << x << endl

#define ll long long
#define MOD 999911659
#define MAXN 500

ll n, P, all, cnt[MAXN + 5], inv[10]; // all代表长度为n的全1串模p的值
int st, len, pos[MAXN + 5], f[10][MAXN + 5][MAXN + 5];

void add(int &x, int y) {
    x = (x + y) % MOD;
    if(x < 0) x += MOD;
}

int Mul(int x, int y) {
    return 1LL * x * y % MOD;
}

int fpow(int x, int p) {
    int ret = 1;
    while (p) {
        if (p & 1) ret = Mul(ret, x);
        x = Mul(x, x);
        p >>= 1;
    }
    return ret;
}

int C(ll n, ll m) {
    if (n < m) return 0;
    int ret = 1;
    for (ll x = n, y = 1; y <= m; --x, ++y)
        ret = Mul(ret, Mul(x % MOD, inv[y]));
    return ret;
}

int main() {
    cin >> n >> P;
    if (n <= P) { // 预处理循环节
        int sum = 0;
        for (int i = 1; i <= n; ++i) {
            sum = (sum * 10 + 1) % P;
            cnt[sum]++;
        }
        all = sum;
    }
    else {
        int sum = 0;
        for (int i = 1; i <= P + 1; ++i) {
            sum = (sum * 10 + 1) % P;
            if (cnt[sum]) {
                st = pos[sum], len = i - pos[sum];
                break;
            }
            pos[sum] = i, cnt[sum]++;
        }
        for (int i = 0; i < P; ++i)
            if (cnt[i] && pos[i] >= st) {
                cnt[i] = (n - st + 1) / len;
                if (pos[i] - st + 1 <= (n - st + 1) % len) cnt[i]++;
                if ((pos[i] - st + 1) % len == (n - st + 1) % len) all = i;
            }
    }
    for (int i = 1; i <= 8; ++i) inv[i] = fpow(i, MOD - 2);
    f[0][0][all] = 1; // 初始化
    for (int r = 0; r < P; ++r) {
        for (int i = 0; i <= 8; ++i)
            for (int k = 0; k < P; ++k)
                for (int j = 0; j + i <= 8; ++j)
                    if(cnt[r]) add(f[i + j][r + 1][(k + j * r % P) % P], Mul(C(cnt[r] + j - 1, j), f[i][r][k]));
                    else f[i][r + 1][k] = f[i][r][k]; // 注意这里,要把值继承过来
    }
    int ans = 0;
    for (int i = 0; i <= 8; ++i)
        add(ans, f[i][P][0]);
    printf("%d\n", ans);
    return 0;
}