您的位置:首页 > 其它

bzoj 2323: [ZJOI2011]细胞

2018-02-26 19:15 323 查看

题面

题意

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

做法

经过分析之后,可以发现分段与去掉丝两部相互独立,而且退化这一步的种类数恰好是斐波那契数,因为数字大小是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
;
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
,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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: