bzoj2323[ZJOI2011]细胞 **

来源:互联网 发布:网络知识产权专利申请 编辑:程序博客网 时间:2024/05/16 08:39

题目链接:bzoj2323
题目大意:
给一个有n个数字的数字串,把这个整体看成有质量的小球。然后有三步事情要做:
1、划分数字串,划分成k串,均分成k个质量相等的小球,把划分出的数字连起来作为一个十进制的数ai标在小球上;
2、分割小球,把第i个小球均分成ai份;
3、连接小小球。除最外两端,其余的不允许单独存在。
问最终有多少种不同的结构。
两种结构被认为相同,当且仅当他们拥有相同个数的小球体,从头到尾每个小球体的质量相同,并且从头到尾每对相邻小球体之间的连接方式相同

题解:
矩阵乘法+dp
这题厉害了..根本想不出来..

随便想一想就可以知道如果第一步是不同的,那么之后的得到所有结构一定不同。可以自己举举栗子。第二步的意思就是第一步分成了一些数,小球的总个数就是那些数加起来的和。
而第三步的话,假设分成了很多个小球。那么要得到不同的连接方式的个数就相当于从中间的连接中选择一些来去掉,要求选出来的不相邻。跟做过的一道组合数学题很像..额noip2016提高组初赛填空题就有一道一样的模型,这个就是斐波那契。但是因为最外面两端的连接是不能选的,所以要-2。于是就可以用矩阵来搞。但是你会发现,这些小球的个数很大很大最多可以有一千位,快速幂也救不了。

这个时候我们可以考虑拆位。
A是斐波那契矩阵。假设需要求两位数xy的幂,那么可以有

A10x+y=(A10)x×Ay
所以我们可以预处理出(A10)n,这样
Anum=((A10ij)a[j]×(A10ij+1)a[j1]×

num为从j到i连起来的数,a[x]就表示原串中第x个数是什么。】

f[i]表示分前i个位的方案数。于是容易得到方程:

f[i]=j=1if[j1]×Anum
即枚举最后一段从哪里开始分,其结果就是分得的这段j~i所能得到的稳定方案数乘上j之前分得的方案数。最后的结果就是f[n2]了。
//为了方便我一开始初始化的时侯把f[0]清成了f[-2]那样的,所以结果直接就是f[n]

#include<cstdio>#include<cstdlib>#include<cstring>#include<algorithm>#include<iostream>using namespace std;typedef long long LL;#define maxn 1010const LL mod=1000000007;int n;char s[maxn];struct mrtx{    LL a[2][2];int l,r;    mrtx(){memset(a,0,sizeof(a));}}A[maxn],f[maxn];mrtx multt(mrtx x,mrtx y){    mrtx ret;ret.l=x.l;ret.r=y.r;    for (int i=0;i<x.l;i++)     for (int j=0;j<y.r;j++)     {         for (int k=0;k<x.r;k++)          ret.a[i][j]+=x.a[i][k]*y.a[k][j];        ret.a[i][j]%=mod;     }    return ret;}mrtx pluss(mrtx x,mrtx y){    mrtx ret;ret.l=x.l;ret.r=x.r;    for (int i=0;i<x.l;i++)     for (int j=0;j<x.r;j++)      ret.a[i][j]=(x.a[i][j]+y.a[i][j])%mod;    return ret;}mrtx qpow(mrtx x,int tt){    mrtx ret;ret.l=ret.r=2;    ret.a[0][0]=ret.a[1][1]=1;    while (tt)    {        if (tt&1) ret=multt(ret,x);        x=multt(x,x);tt>>=1;    }return ret;}int main(){    //freopen("a.in","r",stdin);    //freopen("a.out","w",stdout);    scanf("%d\n",&n);gets(s+1);    A[0].l=A[0].r=2;    A[0].a[0][0]=A[0].a[0][1]=A[0].a[1][0]=1;    for (int i=1;i<=n;i++) A[i]=qpow(A[i-1],10);    f[0].l=2;f[0].r=1;f[0].a[0][0]=1;f[0].a[1][0]=-1;    for (int i=1;i<=n;i++)    {        mrtx now;now.l=now.r=2;        now.a[0][0]=now.a[1][1]=1;        for (int j=i;j>0;j--)        {            int x=s[j]-'0';            now=multt(now,qpow(A[i-j],x));            f[i]=pluss(multt(now,f[j-1]),f[i]);        }    }    printf("%lld\n",(f[n].a[0][0]+mod)%mod);    return 0;}
0 0
原创粉丝点击