qwq真的是一道好题qwq自己做基本是必不可能做出来的。

首先,如果这个题目只是求一个\(f\)数组的话,那就是一道裸题。

首先,根据样例 根据题目描述,我们能发现其实同样数字的不同排列,也是属于不同的方案的,那统计起来其实方便很多。

首先我们发现,对于\(i\)这个数,他可以拆出来\([1,m]\)任何一个数,接在对应的\(f[i-1]到f[i-m]\)

也就是说\(f[i]=f[i-1]+f[i-2]+f[i-3]....f[i-m]\)

qwq那我们可以构造出转移矩阵

以\(m=3\)为栗子

0 0 1
1 0 1
0 1 1

我们假设这个转移矩阵是\(a\)

那我们就可以直接将每一个\(f[i]\)转化成,初始矩阵\(\times a^i\)的形式。

qwq但这个离我们求出来\(g\)数组还差好远。

由于\(g\)数组涉及\(f\)数组的拆分形式。

那我们不妨观察一下\(f[i+j]\)等于多少。

\[f[i+j]=a^{i+j} = a^i \times a^j = f[i]*f[j]
\]

那么我们就可以直接用矩阵乘法的形式来表示拼接了。

那g数组的转移式子,也就比较好求了

\[g[i]=\sum_{j=0}^{i-1}g[j]*d[j+1][i]
\]

其中\(d[j+1][i]\)表示\([j+1,i]\)这些数从左到右排起来,组成的数的\(f\)的对应矩阵是多少。

这里转移式子的意义是,我们对于当前位,考虑枚举所有他的后缀,和前面任意的\(f[i]\)的值乘起来,都是一个合法的方案(原理根据上面对\(f[i+j]\)的讨论)。

之所以能直接用\(g\)数组来乘转移矩阵而不是一个一个分别乘。

是因为同大小的矩阵具有乘法分配律!

所以\(g\)也就可以直接和对应矩阵乘起来了

那么最后的\(ans\),就应该是初始矩阵

还是以\(m=3\)为例

0 0 1
0 0 0
0 0 0

乘上\(g[n]\)之后第一行最后一个元素的值,也就相当于\(g[n]\)的第m行m列的那个元素。

现在整个问题的瓶颈到了怎么求\(d\)数组,由于数值太大,所以我们没有办法直接快速乘。

qwq

现在考虑递推

我们令\(b[i][j]\)表示\(i\times 10^j\)的对应的f的转移矩阵是多少。

比较容易发现这个数组还是很好递推的。

每次\(b[i][j]=qsm(b[i][j-1],10)\)

那知道这个数组,其实\(d\)数组也就不难推了

我们首先令\(d[i][i]=b[s[i]-'0'][0]\)

那么\(d[j][i]=d[j+1][i]*b[s[j]-'0'][i-j]\)

那么到这里这个问题也就基本解决了。

感觉细节真的是很多。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define mk make_pair
#define ll long long
#define int long long
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while (!isdigit(ch)) {if (ch=='-') f=-1;ch=getchar();}
while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return x*f;
}
const int maxn = 7;
const int mod = 998244353;
struct Ju{
int x,y;
int a[maxn][maxn];
Ju operator * (Ju b)
{
Ju ans;
memset(ans.a,0,sizeof(ans.a));
ans.x=x;
ans.y=b.y;
for (int i=1;i<=ans.x;i++)
for (int j=1;j<=ans.y;j++)
for (int k=1;k<=y;k++)
ans.a[i][j]=(ans.a[i][j]+a[i][k]*b.a[k][j]%mod)%mod;
return ans;
}
Ju operator +(Ju b)
{
Ju ans;
memcpy(ans.a,a,sizeof(ans.a));
ans.x=x;
ans.y=y;
for (int i=1;i<=x;i++)
for (int j=1;j<=y;j++)
ans.a[i][j]=(ans.a[i][j]+b.a[i][j])%mod;
return ans;
}
};
Ju qsm(Ju i,int j)
{
Ju ans;
memset(ans.a,0,sizeof(ans.a));
ans.x=i.x;
ans.y=i.y;
for (int p=1;p<=i.x;p++) ans.a[p][p]=1;
while (j)
{
if (j&1) ans=ans*i;
i=i*i;
j>>=1;
}
return ans;
}
char s[2020];
int n,m;
Ju a[510][510]; //a[i][j]表示,区间[i,j]的数的矩阵是多少
Ju ymh[510][510]; //ymh[i][j]表示,i*10^j的矩阵是多少
Ju lyf;
Ju g[510];//大小相同的矩阵乘法具有分配律
void init() //初始化矩阵的行和列
{
for (int i=0;i<=9;i++)
for (int j=0;j<=n;j++)
{
ymh[i][j].x=ymh[i][j].y=m;
}
for (int i=0;i<=n;i++)
for (int j=0;j<=n;j++)
{
a[i][j].x=a[i][j].y=m;
}
for (int i=0;i<=n;i++) g[i].x=g[i].y=m;
}
void print(Ju a)
{
cout<<"*******"<<endl;
cout<<a.x<<" "<<a.y<<endl;
for (int i=1;i<=a.x;i++)
{
for (int j=1;j<=a.y;j++)
cout<<a.a[i][j]<<" ";
cout<<endl;
}
}
signed main()
{
init();
scanf("%s",s+1);
m=read();
n=strlen(s+1);
init();
ymh[0][0].x=m;
ymh[0][0].y=m;
for (int i=1;i<=m;i++) ymh[0][0].a[i][i]=1; //设置初始矩阵
lyf.x=m;
lyf.y=m;
for (int i=1;i<m;i++) lyf.a[i+1][i]=1;
for (int i=1;i<=m;i++) lyf.a[i][m]=1; //设置转移矩阵 for (int i=1;i<=9;i++)
ymh[i][0]=ymh[i-1][0]*lyf; //先递推出来所有i*10^0的答案
for (int i=0;i<=9;i++)
for (int j=1;j<=n;j++)
ymh[i][j]=qsm(ymh[i][j-1],10); //预处理出来所有的值
// print(ymh[1][0]);
// print(ymh[0][0]);
for (int i=n;i>=1;i--)
{
a[i][i]=ymh[s[i]-'0'][0];
for (int j=i-1;j>=1;j--)
{
a[j][i]=ymh[s[j]-'0'][i-j]*a[j+1][i];
}
} //预处理a数组
//print(a[1][1]);
g[0]=ymh[0][0];
for (int i=1;i<=n;i++)
{
for (int j=i-1;j>=0;j--)
g[i]=g[i]+(g[j]*a[j+1][i]); //计算g,因为g是个sigma的数组,而且同大小矩阵乘法具有分配律,所以这句话表示,当前[j+1,i]这个串可以和之前任意一种组合,组合成一个串。
}
int ans=0;
//print(g[n]);
//for (int i=1;i<=m;i++) ans=(ans+g[n].a[1][i])%mod;
//cout<<ans<<endl;
cout<<g[n].a[m][m]<<endl;
return 0;
}

洛谷3176 [HAOI2015]数字串拆分 (矩阵乘法+dp)的更多相关文章

  1. loj#2128. 「HAOI2015」数字串拆分 矩阵乘法

    目录 题目链接 题解 代码 题目链接 loj#2128. 「HAOI2015」数字串拆分 题解 \(f(s)\)对于\(f(i) = \sum_{j = i - m}^{i - 1}f(j)\) 这个 ...

  2. bzoj 4037: [HAOI2015]数字串拆分【dp+矩阵加速】

    首先f长得就很像能矩阵优化的,先构造转移矩阵(这里有一点神奇的地方,我看网上的blog和我构造的矩阵完全不一样还以为我的构造能力又丧失了,后来惊奇的发现我把那篇blog里的构造矩阵部分换成我的构造方式 ...

  3. [HAOI2015]数字串拆分

    题目描述 你有一个长度为n的数字串.定义f(S)为将S拆分成若干个1~m的数的和的方案数,比如m=2时,f(4)=5,分别为4=1+1+1+1你可以将这个数字串分割成若干个数字(允许前导0),将他们加 ...

  4. BZOJ4037:[HAOI2015]数字串拆分——题解

    https://www.lydsy.com/JudgeOnline/problem.php?id=4037 你有一个长度为n的数字串.定义f(S)为将S拆分成若干个1~m的数的和的方案数,比如m=2时 ...

  5. bzoj4037 [HAOI2015]数字串拆分

    Description 你有一个长度为n的数字串.定义f(S)为将S拆分成若干个1~m的数的和的方案数,比如m=2时,f(4)=5,分别为4=1+1+1+1你可以将这个数字串分割成若干个数字(允许前导 ...

  6. BZOJ 4037 [HAOI2015]数字串拆分 ——动态规划

    拆分的情况下,发现f数组本身并不是很好递推. 因为f(123)=f(123)/f(12+3)/f(1+2+3). 然后考虑f可以怎么表示f(n)=a0*M^n M为转移矩阵. 然后发现 f(x+y)= ...

  7. 【LOJ】#2128. 「HAOI2015」数字串拆分

    题解 题中给的函数可以用矩阵快速幂递推 我们记一个数组dp[i](这个数组每个元素是一个矩阵)表示从1到i所有的数字经过拆分矩阵递推的加和 转移方法是 \(dp[i] = \sum_{j = 0}^{ ...

  8. 「区间DP」「洛谷P1043」数字游戏

    「洛谷P1043」数字游戏 日后再写 代码 /*#!/bin/sh dir=$GEDIT_CURRENT_DOCUMENT_DIR name=$GEDIT_CURRENT_DOCUMENT_NAME ...

  9. 洛谷 P3177 [HAOI2015]树上染色 树形DP

    洛谷 P3177 [HAOI2015]树上染色 树形DP 题目描述 有一棵点数为 \(n\) 的树,树边有边权.给你一个在 \(0 \sim n\)之内的正整数 \(k\) ,你要在这棵树中选择 \( ...

随机推荐

  1. cmd关闭端口占用

    netstat -nao |findStr "8080" taskkill /pid 15406  /f

  2. 移动端动画——requestAnimationFrame

    window.requestAnimationFrame() 告诉浏览器--你希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画.该方法需要传入一个回调函数作为参数,该回调函数会 ...

  3. Python语法之选择和循环(if、while)

    前言:在程序开发中,一共有三种流程方式: 顺序:从上向下,顺序执行代码 分支:根据条件判断,决定执行代码的分支 循环:让特定代码重复执行(解决程序员重复工作) 1.判断的定义: 如果条件满足,才能做某 ...

  4. C# AutoMaper使用自定义主键

    有时候实际业务中主键不一定叫Id,比如示例数据库Northwind中的RegionID,TerritoryID等,本示例用Abp框架并以Northwind数据库Region表为数据依据 一.在Core ...

  5. freeswitch刷新网关方法

    1.freeswitch xml配置文件新增网关后,使其生效,可以重启freeswitch或者使用命令方式 fs_cli -H 127.0.0.1 -P 8021 -p hmzj -x sofia p ...

  6. 并发编程之:ForkJoin

    大家好,我是小黑,一个在互联网苟且偷生的农民工. 在JDK1.7中引入了一种新的Fork/Join线程池,它可以将一个大的任务拆分成多个小的任务并行执行并汇总执行结果. Fork/Join采用的是分而 ...

  7. python安装easyinstall/pip出错

    在Windows中装了python3.6,自然还要装pip.按度娘的提供的方法先下载easyinstall,然后在CMD下输入: python ez_setup.py 结果报错 ........... ...

  8. PHP中的一些杂项函数学习

    今天来学习的这些杂项函数在官方文档中都是放在一个杂项扩展中的,其实这些函数都并不是什么什么特别的扩展,也不需要安装什么东西就可以使用,它们就是一些简单的功能函数而已.当然,其中不少内容也是我们经常会使 ...

  9. 在PHP中操作文件的扩展属性

    在操作系统的文件中,还存在着一种我们可以自己定义的文件属性.这些属性不是保存在文件内容中,也不是直接可以通过 ls -al 所能看到的内容.它们可以将一个键值对信息永久得关联到文件上,一般现在的 Li ...

  10. npm WARN ajv-keywords@2.1.1 requires a peer of ajv@^5.0.0 but none is installed. You must install peer dependencies yourself.

    解决: npm install -g npm-install-peers npm install -g npm npm i ajv 但是好像没啥用