首页 > 代码库 > bzoj4037 [HAOI2015]数字串拆分

bzoj4037 [HAOI2015]数字串拆分

传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=4037

【题解】

我们发现容易得出递推式:f[i] = Σf[i-j] (1<=j<=m)

那么就能矩阵乘法了。容易构造转移矩阵:

如果是5*5的大概是这样:

0 1 0 0 0

0 0 1 0 0

0 0 0 1 0

0 0 0 0 1

1 1 1 1 1

然后乘一个初始矩阵

1

1

2

4

8

(前5个的f值)

那么设转移矩阵A,初始矩阵B,那么A^n*B就是f(n)的值了。

现在要求一坨f(n)值的和

那么我们发现一坨的和就是(A^n1+A^n2+A^n3+...)*B

那么我们再次考虑dp,令g[i]表示到了第i个位置,题目那坨式子的和。

那么g[i] = Σ(g[j] * trans(j+1, i)),(0<=j<i),其中trans(l,r)表示字符串[l,r]区间内组成的数为指数,转移矩阵的次幂。

那么我们可以预处理转移矩阵的i*10^x次幂记作num[x+1][i]

每次明显我们是在高位添加一位,那么我们直接暴力乘即可。

最后记得乘B。

复杂度O(n^2m^3)

技术分享
# include <stdio.h>
# include <string.h>
# include <algorithm>
// # include <bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
const int M = 5e2 + 10;
const int mod = 998244353;

# define RG register
# define ST static

# include <assert.h>
struct Matrix {
    int a[8][8], n, m;
    inline void set(int _n, int _m) {
        n = _n, m = _m;
        memset(a, 0, sizeof a);
    }
    friend Matrix operator + (Matrix a, Matrix b) {
        assert(a.n == b.n && a.m == b.m);
        Matrix c; c.set(a.n, a.m);
        for (int i=1; i<=c.n; ++i)
            for (int j=1; j<=c.m; ++j) {
                c.a[i][j] = a.a[i][j] + b.a[i][j];
                if(c.a[i][j] >= mod) c.a[i][j] -= mod;
            }
        return c;
    }
    friend Matrix operator * (Matrix a, Matrix b) {
        assert(a.m == b.n);
        Matrix c; c.set(a.n, b.m);
        for (int i=1; i<=c.n; ++i)
            for (int j=1; j<=c.m; ++j)
                for (int k=1; k<=a.m; ++k) {
                    c.a[i][j] += 1ll * a.a[i][k] * b.a[k][j] % mod;
                    if(c.a[i][j] >= mod) c.a[i][j] -= mod;
                }
        return c;
    }
    friend Matrix operator ^ (Matrix a, int b) {
        assert(a.n == a.m);
        Matrix c; c.set(a.n, a.m);
        for (int i=1; i<=a.n; ++i) c.a[i][i] = 1;
        while(b) {
            if(b&1) c = c * a;
            a = a * a;
            b >>= 1;
        }
        return c;
    }
};

Matrix A, f[M], base[M][10], B;
char str[M];
int m, n[M], nn = 0;
int s[] = {0, 1, 1, 2, 4, 8};

int main() {
    scanf("%s%d", str, &m);
    for (int i=0; str[i]; ++i) n[++nn] = str[i] - 0;
    A.set(m, m); B.set(m, 1);
    for (int i=1; i<m; ++i) A.a[i][i+1] = 1;
    for (int i=1; i<=m; ++i) A.a[m][i] = 1, B.a[i][1] = s[i];    
    for (int i=1; i<=nn; ++i) {
        base[i][0].set(m, m);
        for (int j=1; j<=m; ++j) base[i][0].a[j][j] = 1;
        for (int j=1; j<=9; ++j) 
            base[i][j] = base[i][j-1] * A;
        A = A^10;
    }
    
    f[0].set(m, m);
    for (int i=1; i<=m; ++i) f[0].a[i][i] = 1;
    for (int i=1; i<=nn; ++i) {
        Matrix t; t.set(m, m); f[i].set(m, m);
        for (int j=1; j<=m; ++j) t.a[j][j] = 1;
        for (int j=i-1; ~j; --j) {
            t = t * base[i-j][n[j+1]];
            f[i] = f[i] + f[j] * t;
        }
    }
    B = f[nn] * B;
    printf("%d\n", B.a[1][1]);
    return 0;
}
View Code

 

bzoj4037 [HAOI2015]数字串拆分