bzoj 2323: [ZJOI2011]细胞

题面

题意

给出一个数字串表示一个细胞,将其分为若干段等大小球,每个球内有一段数字,每一段再分为该数字大小个小球,每个小球与两边的有丝连接(除了两头的),每一个小球至少退化一条丝,那么有几种不同的分法.

做法

经过分析之后,可以发现分段与去掉丝两部相互独立,而且退化这一步的种类数恰好是斐波那契数,因为数字大小是10^100级别的,因此要用矩阵快速幂来写,然后问题就在与分段.
可以用dp来实现,dp[i]表示i到n这一段的种类数,那么dp[i]转移到dp[i-1]可以枚举以i-1为左端点的所有数字段,该数字段的种类数与剩余部分的种类数(已用dp记录)的积之和即为dp[i-1].
最后dp[1]就是答案.

代码

实现起来并不是很长

#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long
#define N 1010
#define M 1000000007
using namespace std;

ll n;
char str[N];
struct Jz
{
    ll num[3][3];
    Jz()
    {
        num[2][2]=num[1][1]=1;
        num[1][2]=num[2][1]=0;
    }
    void ql()
    {
        memset(num,0,sizeof(num));
    }
    Jz operator + (const Jz &u) const
    {
        Jz res;
        ll i,j;
        for(i=1;i<=2;i++)
        {
            for(j=1;j<=2;j++)
            {
                res.num[i][j]=(num[i][j]+u.num[i][j])%M;
            }
        }
        return res;
    }
    Jz operator * (const Jz &u) const
    {
        Jz res;
        ll i,j,k;
        res.ql();
        for(i=1;i<=2;i++)
        {
            for(j=1;j<=2;j++)
            {
                for(k=1;k<=2;k++)
                {
                    res.num[i][j]+=num[i][k]*u.num[k][j]%M;
                    res.num[i][j]%=M;
                }
            }
        }
        return res;
    }
    Jz pw(ll u)
    {
        Jz res,v;
        v=*this;
        for(;u>0;)
        {
            if(u&1) res=res*v;
            v=v*v;
            u>>=1;
        }
        return res;
    }
};
Jz dp[N],fb;

int main()
{
    ll i,j;
    scanf("%lld%s",&n,str+1);
    fb.num[1][1]=0;
    fb.num[1][2]=fb.num[2][2]=fb.num[2][1]=1;
    for(i=n;i>=1;i--)
    {
        Jz now;
        dp[i].ql();
        for(j=i;j<=n;j++)
        {
            now=now.pw(10)*fb.pw(str[j]-48);
            dp[i]=dp[i]+dp[j+1]*now;
        }
    }
    cout<<dp[1].num[1][1]%M;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值