[ZJOI2011]细胞——斐波那契数列+矩阵加速+dp

Description

bzoj2323

Solution

题目看起来非常复杂。

本质不同的细胞这个条件显然太啰嗦,

是否有些可以挖掘的性质?

1.发现,只要第一次分裂不同,那么互相之间一定是不同的(即使总数目相同)。

所以先考虑第一次分裂后,一个固定小球体数量的情况:

2.第一次分裂后,最后的小球体数量固定。想要方案数不同,必须连接方式不同。

可以列出dp式子,f[n](以n结尾砍一刀)=f[n-2]+f[n-3]+...+f[2]+f[0],而f[0]=1,f[1]=0

而fibo[n]-1=f[n-2]+...+f[1]

可以猜想和斐波那契数列有关。

实际上,f[n]=fibo[n-1],f[1]=0,f[0]=1

所以,如果我们知道第一次分裂的方案,

即S=a1+...+am(分成m段,ai表示这一段拼成的数字)

那么,f[S]=fibo[S-1]

好了,

所以,题意其实是:

? 给出一个数字序列
? 你要先把序列切成很多段,把每一段看做一个十进制数
??1, ??2, … , ????
? 你要求每一种分割方案的 Fib (??1 + ? + ???? )的和
? ?? ≤ 1000

现在我们的问题就是怎样快速统计这些所有fibo的和。

设f[i]表示,前i个,i后劈一刀,所有产生的Fib (??1 + ? + ??k )的和。

由于矩阵有结合律和分配律,

Fibo[i]=Start*(M^j*M^(i-j))(M是转移矩阵)

我可以把f用矩阵表示

经典n^2的dp

然后,我们要快速统计M^(num[j...i])

直接每次高精精快速幂??(逗笑)

根据套路,可以利用j每次向左走一位,在路上利用之前的基础统计出num[j...i]

那么,我么就要知道所有的K=a*10^p,M^K可以预处理。

然后直接M^num=M^(K+old)=M^k*M^old

即可统计出num[j...i]

转移的时候,f[i]+=f[j]*M^(num[j+1...i])即可,因为乘法分配律,dp式子成立。

注意下,f[S]=Fibo[S-1]

所以,其实f[0]=[0,1,0,0](写成矩阵)

总之,如果j==0,那么这个段是第一段,直接把这个1减掉。相当于对矩阵乘一个逆

代码:

(注意,逆矩阵:[mod-1,1,1,0]而不是[-1,1,1,0]。不要爆负数)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1005;
const int mod=1e9+7;
int n;
char s[N];
struct tr{
    ll a[3][3];
    void pre(){
        memset(a,0,sizeof a);
    }
    void init(){
        a[1][1]=a[2][2]=1;
    }
    tr operator *(const tr &b){
        tr c;c.pre();
        for(int i=1;i<=2;i++)
         for(int k=1;k<=2;k++)
          for(int j=1;j<=2;j++)
           c.a[i][j]=(c.a[i][j]+a[i][k]*b.a[k][j])%mod;
        return c;
    }
    tr operator +(const tr &b){
        tr c;c.pre();
        for(int i=1;i<=2;i++)
         for(int j=1;j<=2;j++)
          c.a[i][j]=(a[i][j]+b.a[i][j])%mod;
        return c;
    }
    void op(){
        cout<<endl;
        for(int i=1;i<=2;i++){
            for(int j=1;j<=2;j++){
                cout<<a[i][j]<<" ";
            }cout<<endl;
        }
        cout<<endl;
    }
}B[10][1005],f[1005],A,C,D;
tr qm(tr c,ll y){
    tr ret;ret.pre();ret.init();
    while(y){
        if(y&1) ret=ret*c;
        c=c*c;
        y>>=1;
    }
    return ret;
}
void prewrk(){
    for(int i=0;i<=9;i++) B[i][0]=qm(A,i);
    for(int i=1;i<=n;i++){
        for(int j=0;j<=9;j++){
            B[j][i]=qm(B[j][i-1],10);
        }
    }
}
int main(){
    scanf("%d",&n);
    A.a[1][1]=0;A.a[1][2]=1;A.a[2][2]=1;A.a[2][1]=1;
    D.a[1][1]=mod-1;D.a[1][2]=1;D.a[2][1]=1;D.a[2][2]=0;
    prewrk();
    scanf("%s",s+1);
    //cout<<" hh "<<endl;
    f[0].a[1][2]=1;
    for(int i=1;i<=n;i++){
        C.pre();C.init();
        //cout<<i<<"------------"<<endl;
        for(int j=i-1;j>=0;j--){
            //cout<<s[j+1]-‘0‘<<endl;
            C=C*B[s[j+1]-‘0‘][i-j-1];
            //C.op();
            //cout<<">>"<<endl;
            if(j==0) C=C*D;
            tr tmp=f[j]*C;
            f[i]=f[i]+tmp;
            //cout<<" f[i] "<<endl;
            //f[i].op();
        }
    }
    printf("%lld\n",f[n].a[1][1]);
    return 0;
}

原文地址:https://www.cnblogs.com/Miracevin/p/9748231.html

时间: 2024-08-03 19:58:03

[ZJOI2011]细胞——斐波那契数列+矩阵加速+dp的相关文章

HDU 4549 M斐波那契数列 ( 矩阵快速幂 + 费马小定理 )

HDU 4549 M斐波那契数列 (  矩阵快速幂 + 费马小定理  ) 题意:中文题,不解释 分析:最好的分析就是先推一推前几项,看看有什么规律 #include <cstdio> #include <cstring> #include <algorithm> using namespace std; typedef __int64 LL; #define CLR( a, b ) memset( a, b, sizeof(a) ) #define MOD 100000

HDU 4549 M斐波那契数列(矩阵快速幂&amp;费马小定理)

ps:今天和战友聊到矩阵快速幂,想到前几天学长推荐去刷矩阵专题,挑了其中唯一一道中文题,没想到越过山却被河挡住去路... 题目链接:[kuangbin带你飞]专题十九 矩阵 R - M斐波那契数列 Time Limit:1000MS Memory Limit:32768KB 64bit IO Format:%I64d & %I64u 题意 Description M斐波那契数列F[n]是一种整数数列,它的定义如下: F[0] = a F[1] = b F[n] = F[n-1] * F[n-2]

斐波那契数列——矩阵的幂求解

题目: 斐波那契数列的递推公式如下: F(0) = 0; F(1) = 1; F(n + 2) = F(n + 1) + F(n); 求数列的第N项的值对10000取余的结果.( 0<=n<= 10^16) 求解斐波那契数列,如果N比较小的情况下,可以直接打表求解,但是对于N很大的情况下,并不适用. 所以,有些人会想到高精度计算,但是,N达到10^5以上时,时间复杂度难以想象,每计算一个数,需要进行高精度加法.然而还有求解对10000的取余的值. 我们可以用矩阵的幂来求解.斐波那契数列的递推公

[codevs]1250斐波那契数列&lt;矩阵dp&gt;

题目描述 Description 定义:f0=f1=1, fn=fn-1+fn-2(n>=2).{fi}称为Fibonacci数列. 输入n,求fn mod q.其中1<=q<=30000. 输入描述 Input Description 第一行一个数T(1<=T<=10000). 以下T行,每行两个数,n,q(n<=109, 1<=q<=30000) 输出描述 Output Description 文件包含T行,每行对应一个答案. 样例输入 Sample I

洛谷P1962 斐波那契数列 || P1349 广义斐波那契数列[矩阵乘法]

P1962 斐波那契数列 大家都知道,斐波那契数列是满足如下性质的一个数列: • f(1) = 1 • f(2) = 1 • f(n) = f(n-1) + f(n-2) (n ≥ 2 且 n 为整数) 题目描述 请你求出 f(n) mod 1000000007 的值. 输入输出格式 输入格式: ·第 1 行:一个整数 n 输出格式: 第 1 行: f(n) mod 1000000007 的值 输入输出样例 输入样例#1: 5 输出样例#1: 5 输入样例#2: 10 输出样例#2: 55 说明

UVA10299- Modular Fibonacci(斐波那契数列+矩阵快速幂)

题目链接 题意:给出n和m,求出f(n) % m, f(x)为斐波那契数列. 思路:因为n挺大的,如果直接利用公式计算很有可能会TLE,所以利用矩阵快速幂求解,|(1, 1), (1, 0)| * |f(n - 1), f(n - 2)| = |f(n), f(n - 1)|,所以求f(n)相当于|f(1), f(0)|乘上n - 1次的|(1, 1), (1, 0)|. 代码: #include <iostream> #include <cstdio> #include <

【题解】佳佳的斐波那契数列(矩阵)

[题解]佳佳的斐波那契数列 题目描述 求\(\Sigma i\times fib[i]\)的值 数据范围 \(n\le2^{31}-1\) \(Solution\) 看数据范围就会做的题... \[ 设 \f(n):=ib[n] \g(n)=nf(n) \s(n)=\Sigma^{i\le n}_{i=1}g(n) \] 然后 \[ f(i)=f(i-1)+f(i-2) \s(i)=s(i-1)+g(i) \] 不好求\(g\),但是可以直接这样 \[ g(i)=i\times f(i)\=i\

P1349 广义斐波那契数列(矩阵乘法)

题目 P1349 广义斐波那契数列 解析 把普通的矩阵乘法求斐波那契数列改一改,随便一推就出来了 \[\begin{bmatrix}f_2\\f_1 \end{bmatrix}\begin{bmatrix} p&q\1&0\\end{bmatrix}^{n-2}=\begin{bmatrix}f_n\\f_{n-1} \end{bmatrix}\] 水题 代码 #include <bits/stdc++.h> #define int long long using namesp

HDU4549 M斐波那契数列(矩阵快速幂+费马小定理)

Problem Description M斐波那契数列F[n]是一种整数数列,它的定义如下:F[0] = aF[1] = bF[n] = F[n-1] * F[n-2] ( n > 1 )现在给出a, b, n,你能求出F[n]的值吗? Input 输入包含多组测试数据:每组数据占一行,包含3个整数a, b, n( 0 <= a, b, n <= 10^9 ) Output 对每组测试数据请输出一个整数F[n],由于F[n]可能很大,你只需输出F[n]对1000000007取模后的值即可