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;
}

时间: 2024-08-07 13:28:51

bzoj4037 [HAOI2015]数字串拆分的相关文章

[HAOI2015] 数字串拆分 - dp,矩阵乘法

有一个长度为 \(n\) 的数字串,定义 \(f(S)\) 为将 \(S\) 拆分为若干个 \([1,m]\) 的数的和的方案数.现在,你可以将这个数字串分割为若干个数字(允许前导 \(0\))并相加,求所有方案的 \(f\) 的和. \(n \leq 500, m \leq 5\) Solution 设 \(f[i]\) 为 \(i\) 的拆分数,那么显然有 \(f[i]=\sum_{j=1}^m f[i-j]\) 显然这个玩意可以用矩阵来转移,构造向量 \(v_i=(f[i],f[i-1],

把数字串变成2012玛雅密码

问题: 玛雅密码是一串由0.1.2组成的密码,这串数字中如果包含2012,就可以解开末日的大门.给定一串由0.1.2组成的字符串,只有相邻的数字可以交换,求通过最少多少次变换可以得到玛雅密码,并给出这串密码. 思路: 经过很久很久的尝试,放弃了一次性拼凑2012的想法,改用预处理得到所有数字范围中符合玛雅密码的部分,再递归遍历给定的数字串,得到该串所有可能的变换,并比较每个变换的结果需要的步数. import sys def find_all_transfers(str): result={}

[华为]在字符串中找出连续最长的数字串

链接:https://www.nowcoder.com/questionTerminal/2c81f88ecd5a4cc395b5308a99afbbec来源:牛客网 样例输出 输出123058789,函数返回值9 输出54761,函数返回值5 接口说明 函数原型: unsignedint Continumax(char** pOutputstr,  char* intputstr) 输入参数:   char* intputstr  输入字符串: 输出参数:   char** pOutputst

在字符串中找出连续最长的数字串

输入描述: 输入一个字符串. 输出描述: 输出字符串中最长的数字字符串和它的长度. 输入例子: abcd12345ed125ss123058789,如果有重复的最大长度的数字串,则一起输出,如1456jk4789,输出14564789,4 输出例子: 123058789,9 思路:先把字符串中的数字串取出来,然后取出长度最大的数字字串并输出,刚开始我使用HashMap的key存储数字字串,value存字串长度,考虑到HashMap存储无序,故改为LInkedHashMap,然后根据Map的val

笔试算法题(37):二叉树的层序遍历 &amp; 最长递增的数字串

出题:要求层序遍历二叉树,从上到下的层次,每一层访问顺序为从左到右,并将节点一次编号,输出如下:如果只要求打印指定的level的节点,应该如何实现. a b  c d  e  f  g h  i  分析: 原始的层序遍历类似于BFS,打印当前访问的节点curNode的序列号,并将其直接子节点放入队列queue中,然后从queue中取出下一个节点,直 到队列为空:此方法仅能按照层序打印所有节点,并不能区分每一层节点的数量:如果需要区分当前层次的节点,和当前层次节点的子节点,可以使用两个队列 que

比较字符串和数字串(轉載)

比较字符串 和数字串 要比较字符 串(类型 C)和数字 文本(类型 N),可以 在逻辑表达 式中使用下 列运算符. <运算符>含 义 CO 仅包 含 CN 不仅 包含 CA 包 含任何 NA 不 包含任何 CS 包 含字符串 NS 不 包含字符串 CP 包含模 式 NP 不包 含模式 因为除类型 N和 C 外,系统不 能执行任何 其它类型转 换,所以, 在进行包含 这些运 算之 一的比较时 ,操作数应 该是类型 N 或 C. 运算符的功 能如下: CO (仅包含) 如果 <F1> 

在字符串中找出连续最长的数字串 在字符串中找出连续最长的数字串,并把这个串的长度返回

写一个函数,它的原形是int continumax(char *outputstr,char *intputstr)功能:在字符串中找出连续最长的数字串,并把这个串的长度返回,并把这个最长数字串付给其中一个函数参数outputstr所指内存.例如:"abcd12345ed125ss123456789"的首地址传给intputstr后,函数将返回9, #include<iostream> using namespace std; int ContinueMax(char *

解决 PHPExcel 长数字串显示为科学计数

解决 PHPExcel 长数字串显示为科学计数 https://github.com/PHPOffice/PHPExcel/ 在excel中如果在一个默认的格中输入或复制超长数字字符串,它会显示为科学计算法,例如身份证号码,解决方法是把表格设置文本格式或在输入前加一个单引号. 使用PHPExcel来生成excel,也会遇到同样的问题,解决方法有三种: 1.设置单元格为文本 $objPHPExcel = new PHPExcel(); $objPHPExcel->setActiveSheetInd

parseInt在IE8转换返回不相等(parseInt(&quot;08&quot;)返回0等以0开头大于7的数字串)

描述 在IE8内核下parseInt("08")返回0,等以0开头大于7的数字串返回的值不相等 解决方法 parseInt当不指定radix时,当以0x开头时,s按照十六进制计算的:如果以0开头且第二位不为x,则s是按照八进制计算的,因为八进制不能有8,9所以报错返回0. 测试有效效果 parseInt("08",10)==8