[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],\dots,f[i-m])^T\),则可设
\[
A=\begin{bmatrix} 1 & 1 & 1 & \dots & 1 \1 & 0 & 0 & \dots & 0 \0 & 1 & 0 & \dots & 0 \\dots & \dots & \dots & \dots & \dots \0 & 0 & 0 & \dots & 0
\end{bmatrix}
\]
于是转移方程可以被描述为 \(v_i=Av_{i-1}\),进一步地,\(v_n=A^nv_0\),其中 \(v_0=(1,0,\dots,0)^T\)

我们可以预处理出 \(P[i][j]=A^{i\cdot 10^j}\),那么现在对于串 \(a_i\),它的 \(A^S=\prod_{i=1}^k P[a_i][k-i]\)

设 \(g[i]\) 表示对字符串处理到 \(i\) 位置的方案对应的矩阵相加,那么答案就是 \(g[n]\) 的第一行第一列,而 \(g[0]=I\),考虑转移
\[
\begin{aligned}
g[i]&=\sum_{j=0}^{i-1} g[j]\cdot A^{s[j+1,i]}
\end{aligned}
\]
而 \(A\) 的次幂项可以倒着递推,即
\[
A^{s[j,i]}=A^{s[j+1,i]} P[s_j][i-j]
\]

#include <bits/stdc++.h>
using namespace std;

#define int long long
const int mod = 998244353;
const int N = 505;

struct matrix {
    int a[7][7]={};
    int n,m;
    void print() {
        for(int i=1;i<=n;i++) {
            for(int j=1;j<=m;j++) {
                cout<<a[i][j]<<", ";
            }
            cout<<endl;
        }
    }
};

matrix I(int n) {
    matrix ret;
    ret.n=n;
    ret.m=n;
    for(int i=1;i<=n;i++) ret.a[i][i]=1;
    return ret;
}

matrix operator * (matrix a, matrix b) {
    matrix ret;
    ret.n = a.n;
    ret.m = b.m;
    for(int i=1;i<=ret.n;i++) {
        for(int j=1;j<=ret.m;j++) {
            for(int k=1;k<=a.m;k++) {
                ret.a[i][j] += a.a[i][k] * b.a[k][j];
                ret.a[i][j] %= mod;
                ret.a[i][j] += mod;
                ret.a[i][j] %= mod;
            }
        }
    }
    return ret;
}

matrix operator + (matrix a, matrix b) {
    matrix ret;
    ret.n = a.n;
    ret.m = a.m;
    for(int i=1;i<=ret.n;i++) {
        for(int j=1;j<=ret.m;j++) {
            ret.a[i][j] = (a.a[i][j] + b.a[i][j] + mod) % mod;
        }
    }
    return ret;
}

int n,m;
char s[N];

matrix p[12][N],g[N],a[N][N];

signed main() {
    ios::sync_with_stdio(false);
    cin>>s+1;
    n=strlen(s+1);
    for(int i=1;i<=n;i++) s[i]-='0';
    cin>>m;
    p[0][0]=I(m);
    p[1][0].n=m;
    p[1][0].m=m;
    for(int i=1;i<=m;i++) p[1][0].a[1][i]=1;
    for(int i=2;i<=m;i++) p[1][0].a[i][i-1]=1;
    for(int j=0;j<=n;j++) {
        p[0][j]=I(m);
        if(j>0) p[1][j]=p[9][j-1]*p[1][j-1];
        for(int i=2;i<=9;i++) p[i][j]=p[i-1][j]*p[1][j];
    }
    for(int i=1;i<=n;i++) {
        a[i+1][i]=I(m);
        for(int j=i;j>=1;--j) a[j][i]=a[j+1][i]*p[s[j]][i-j];
    }
    g[0]=I(m);
    for(int i=1;i<=n;i++) {
        g[i].n=g[i].m=m;
        for(int j=0;j<i;j++) {
            g[i]=g[i]+g[j]*a[j+1][i];
        }
    }
    cout<<g[n].a[1][1];
}

原文地址:https://www.cnblogs.com/mollnn/p/12398270.html

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

[HAOI2015] 数字串拆分 - dp,矩阵乘法的相关文章

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)的值了. 现在要求

『公交线路 状压dp 矩阵乘法加速』

公交线路 Description 小Z所在的城市有N个公交车站,排列在一条长(N-1)km的直线上,从左到右依次编号为1到N,相邻公交车站间的距离均为1km. 作为公交车线路的规划者,小Z调查了市民的需求,决定按下述规则设计线路: 1.设共K辆公交车,则1到K号站作为始发站,N-K+1到N号台作为终点站. 2.每个车站必须被一辆且仅一辆公交车经过(始发站和终点站也算被经过). 3.公交车只能从编号较小的站台驶往编号较大的站台. 4.一辆公交车经过的相邻两个 站台间距离不得超过Pkm. 在最终设计

BZOJ 3329 Xorequ 数字DP+矩阵乘法

标题效果:特定n,乞讨[1,n]内[1,2^n]差多少x满足x^3x=2x x^3x=2x相当于x^2x = 3x 和3x=x+2x 和2x=x<<1 因此x满足条件IFFx&(x<<1)=0 故x的二进制拆分中随意两个1不相邻 令f[i]为i位数中最高位为0的满足条件的数的数量 g[i]为i位数中最高位为1的满足条件的数的数量 则显然有 f[i+1]=f[i]+g[i] g[i+1]=f[i] 于是第一问数位DP 第二问矩阵乘法就可以 #include <cstdi

【BZOJ 2323】 2323: [ZJOI2011]细胞 (DP+矩阵乘法+快速幂*)

2323: [ZJOI2011]细胞 Description 2222年,人类在银河系外的某颗星球上发现了生命,并且携带了一个细胞回到了地球.经过反复研究,人类已经完全掌握了这类细胞的发展规律: 这种细胞最初的形态是"长条形",一端是头,一端是尾,中间是躯干.细胞内部含有一列密码(你可以认为它是这种细胞的DNA).密码是一个长度为n的数字串,且仅含有1~9这9种数字,沿着细胞的躯干从头到尾排列着. 首先,细胞会经历一次分裂.细胞将沿躯干方向分裂成若干个球体,躯干将退化成丝状物,连接着相

BZOJ 3329 Xorequ 数位DP+矩阵乘法

题目大意:给定n,求[1,n]以内以及[1,2^n]以内有多少x满足x^3x=2x x^3x=2x等价于x^2x = 3x 而3x=x+2x 且2x=x<<1 故x满足条件当且仅当x&(x<<1)=0 故x的二进制拆分中任意两个1不相邻 令f[i]为i位数中最高位为0的满足条件的数的数量 g[i]为i位数中最高位为1的满足条件的数的数量 则显然有 f[i+1]=f[i]+g[i] g[i+1]=f[i] 于是第一问数位DP 第二问矩阵乘法即可 #include <cs

【BZOJ-4386】Wycieczki DP + 矩阵乘法

4386: [POI2015]Wycieczki Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 197  Solved: 49[Submit][Status][Discuss] Description 给定一张n个点m条边的带权有向图,每条边的边权只可能是1,2,3中的一种.将所有可能的路径按路径长度排序,请输出第k小的路径的长度,注意路径不一定是简单路径,即可以重复走同一个点. Input 第一行包含三个整数n,m,k(1<=n<=40,1&

ZOJ - 3216:Compositions (DP&amp;矩阵乘法&amp;快速幂)

We consider problems concerning the number of ways in which a number can be written as a sum. If the order of the terms in the sum is taken into account the sum is called a composition and the number of compositions of n is denoted by c(n). Thus, the

[BZOJ1009] [HNOI2008] GT考试 (KMP &amp; dp &amp; 矩阵乘法)

Description 阿申准备报名参加GT考试,准考证号为N位数X1X2....Xn(0<=Xi<=9),他不希望准考证号上出现不吉利的数字. 他的不吉利数学A1A2...Am(0<=Ai<=9)有M位,不出现是指X1X2...Xn中没有恰好一段等于A1A2...Am. A1和X1可以为0 Input 第一行输入N,M,K.接下来一行输入M位的数. N<=10^9,M<=20,K<=1000 Output 阿申想知道不出现不吉利数字的号码有多少种,输出模K取余的

【BZOJ-1009】GT考试 KMP+DP+矩阵乘法+快速幂

1009: [HNOI2008]GT考试 Time Limit: 1 Sec  Memory Limit: 162 MBSubmit: 2745  Solved: 1694[Submit][Status][Discuss] Description 阿申准备报名参加GT考试,准考证号为N位数X1X2....Xn(0<=Xi<=9),他不希望准考证号上出现不吉利的数字.他的不吉利数学A1A2...Am(0<=Ai<=9)有M位,不出现是指X1X2...Xn中没有恰好一段等于A1A2..