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)=a0*M(x+y), 所以只需要对M矩阵进行DP即可。

这样子每一个位置就可以表示为若干转移矩阵的和,然后就可以利用矩阵的相乘进行递推。

最后直接用原向量乘上转移矩阵即可。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
 
#define ll long long
#define F(i,j,k) for (ll i=j;i<=k;++i)
#define D(i,j,k) for (ll i=j;i>=k;--i)
const ll md=998244353;
 
ll m,l;char s[505];
 
struct matrix{
    ll x[6][6];
    void init(){memset(x,0,sizeof x);}
    void build1(){
        init();
        x[1][1]=1;
    }
    void build2(){
        init();
        F(i,1,m) x[i][1]=1;
        F(i,1,m-1) x[i][i+1]=1;
    }
    void build3(){
        init();
        F(i,1,m) x[i][i]=1;
    }
    matrix operator * (matrix b) {
        matrix ret;
        ret.init();
        F(i,1,m) F(j,1,m)
        {
            F(k,1,m)
            ret.x[i][j]=ret.x[i][j]+x[i][k]*b.x[k][j];
            ret.x[i][j]%=md;
        }
        return ret;
    }
    matrix operator + (matrix b) {
        matrix ret;
        ret.init();
        F(i,1,m) F(j,1,m)
            ret.x[i][j]=((ll)x[i][j]+(ll)b.x[i][j])%md;
        return ret;
    }
}dp[505],one,c[11][501],turn,now,ans;
 
int main()
{
    scanf("%s",s+1);l=strlen(s+1);
    scanf("%lld",&m);
    F(i,0,l) dp[i].init();
    one.build1();
    turn.build2();
    c[0][0].build3();
    F(i,1,10) c[i][0]=c[i-1][0]*turn;
    F(i,1,l-1)
    {
        c[0][i]=c[0][i-1];
        c[1][i]=c[10][i-1];
        F(j,2,10)
        {
            c[j][i]=c[j-1][i]*c[10][i-1];
        }
    }
    dp[0].build3();
    F(i,1,l)
    {
        now.build3();
        D(j,i,1)
        {
            now=now*c[s[j]-'0'][i-j];
            dp[i]=dp[i]+dp[j-1]*now;
        }
    }
    ans=dp[l]*one;
    printf("%lld\n",ans.x[1][1]);
}

  

posted @ 2017-03-19 20:12  SfailSth  阅读(431)  评论(0编辑  收藏  举报