「ZJOI2011」细胞 矩阵快速幂

题意:

输入:

包含两行,其中:

第一行为一个正整数\(n\),表示细胞密码的长度。

第二行共\(n\)个数字,为给定的细胞密码,中间没有空格。

输出:

只包含一个整数,为细胞的种数 \(mod 1000000007\)的结果

思路:

对于第一次分裂,如果切的方法不同,那么分裂出来的球体是不同的,因此不必考虑切出来相同的情况对第二次分裂的结果产生影响。

不同是指:

1、分裂出来的小球个数不同

2、个数相同但质量分布不同

证明的话,可以考虑将一段细胞上的密码用\(abcd\)等代替进行分割,用反证法之类的可以证明

实际上考试的时候可以大胆猜测感性证明×

对于第二次分裂及变异的问题可以转化为,有\(x\)个球,\(x-1\)个丝状物,从中选出一些退化满足相邻的必须有至少一个退化。可以定义出\(dp\)状态,\(dp[i][0]\)表示第\(i\)个不退化,\(dp[i][1]\)则表示退化,那么就能得到两条式子:

dp[i][0]=dp[i-1][1]
dp[i][1]=dp[i-1][0]+dp[i-1][1]

上面这条式子其实是斐波那契数列,问题就转化成了求斐波那契数列第\(x-1\)位,快速求斐波那契数列有矩阵快速幂的写法。

对于第二问就可以选择枚举当前切割的位置为\(i\),上一次的位置为\(j\)\(ans[i]=\sum_{j=1}^{i-1}\,ans[j]*val[j+1][i]\)

但是\(val[j+1][i]\)会很大,写高精也会出现问题,那我们可以换个思路,设矩阵为\(k\)

对于\(i-j\)区间内的贡献为\(k^{val[i][j]}\),将式子再进行转换有\(k^{val[i][j]}=(k^{val[i][j-1]})^{10}*k^{s[j]-'0'}\)

这样子递推过来,我们每次只用再乘上最后一位,所以可以用上述的式子预处理出来\(val[i][j]\)

代码:

#include<bits/stdc++.h>
#define M 1005
#define ll long long
#define Mod 1000000007
using namespace std;
int n;
char s[M];
struct P2 {
    struct node {
        ll a[2][2];
        node() {
            memset(a,0,sizeof(a));
        }
        node operator*(const node&_)const {
            node res;
            for(int i=0; i<2; i++)
                for(int j=0; j<2; j++)
                    for(int k=0; k<2; k++)
                        res.a[i][k]=(res.a[i][k]+a[i][j]*_.a[j][k]%Mod)%Mod;
            return res;
        }
        void operator+=(const node&_) {
            for(int i=0; i<2; i++)
                for(int j=0; j<2; j++)
                    a[i][j]=(a[i][j]+_.a[i][j])%Mod;
        }
    } val[1005][1005],dp[1005],w;
    node mul(node a,int y) {
        node res;
        res.a[0][0]=res.a[1][1]=1;
        while(y) {
            if(y&1)res=res*a;
            a=a*a,y>>=1;
        }
        return res;
    }
    void solve() {
        w.a[0][0]=0,w.a[0][1]=1,w.a[1][0]=1,w.a[1][1]=1;//初始化矩阵
        for(int i=1; i<=n; i++) {
            val[i][i]=mul(w,s[i]-'0');
            for(int j=i+1; j<=n; j++)
                val[i][j]=mul(val[i][j-1],10)*mul(w,s[j]-'0');
        }
        for(int i=1; i<=n; i++) {
            dp[i]=val[1][i];
            for(int j=1; j<i; j++)
                dp[i]+=dp[j]*val[j+1][i];
        }
        printf("%lld\n",dp[n].a[0][0]);
    }
} p2;
int main() {
    scanf("%d%s",&n,s+1);
    p2.solve();
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/cly1231/p/11355825.html
今日推荐