[矩阵快速幂优化DP]BZOJ 4037——Str

来源:互联网 发布:自己的域名 编辑:程序博客网 时间:2024/06/01 21:29

题目梗概

你有一个长度为n的数字串。

定义f(S)为将S拆分成若干个的1~m数的和的方案数。

你可以将这个数字串分割成若干个数字(允许前导0),将他们的f()加起来。

比如g(123)=f(1+2+3)+f(1+23)+f(12+3)+f(123)

已知字符串和m后求答案对998244353取模后的值。

解题思路

对于f()函数,可以推出

f(n)=i=max(0,nm)n1f(i)
,转移关系可以构造出矩阵A。

假设我们将S分解为a1,a2,a3...ak,f(S)=Aa1+a2+a3+...+ak,f(s)=Aa1Aa2Aa3...Aak

于是我们就能得到DP,F[i]=F[j]now,在这里now=Aaj+1...i

但是now的次数是高精,所以我预处理A0...910?

推DP时不断更新now就可以了。

#include<cstdio>#include<cstring>#define LL long long#define cl(x) memset(x,0,sizeof(x))using namespace std;const int tt=998244353;int a[505],n,m;struct jz{    LL x[10][10];    jz(int t=0){cl(x);if (t) for (int i=1;i<=m;i++) x[i][i]=1;}    jz operator*(const jz &b)const{        jz c;        for (int i=1;i<=m;i++)        for (int j=1;j<=m;j++)        for (int k=1;k<=m;k++)        (c.x[i][j]+=x[i][k]*b.x[k][j])%=tt;        return c;    }    jz operator+(const jz &b)const{        jz c;        for (int i=1;i<=m;i++)        for (int j=1;j<=m;j++)        c.x[i][j]=(x[i][j]+b.x[i][j])%tt;        return c;    }    void print(){        for (int i=1;i<=m;i++){            for (int j=1;j<=m;j++) printf("%lld ",x[i][j]);            printf("\n");        }    }}F[15][505],f[505];int main(){    freopen("exam.in","r",stdin);    freopen("exam.out","w",stdout);    char ch=getchar();    while (ch<'0'||ch>'9') ch=getchar();    while (ch>='0'&&ch<='9') a[++n]=ch-48,ch=getchar();    scanf("%d",&m);    for (int i=0;i<=n;i++) F[0][i]=jz(1);    for (int i=1;i<=m;i++)    for (int j=1;j<=m;j++)    F[1][0].x[i][j]=i==1||i==j+1;    for (int i=1;i<=n;i++){        F[1][i]=jz(1);        for (int j=1;j<11;j++) F[1][i]=F[1][i]*F[1][i-1];    }    for (int i=2;i<=9;i++)    for (int j=0;j<=n;j++) F[i][j]=F[i-1][j]*F[1][j];    f[0]=jz(1);    for (int i=1;i<=n;i++){        jz now=F[a[i]][0];        for (int j=i-1;j>=0;j--){            f[i]=f[i]+f[j]*now;            if (j) now=now*F[a[j]][i-j];        }    }    printf("%lld\n",f[n].x[1][1]);    return 0;}
原创粉丝点击