[国家集训队]整数的lqp拆分——生成函数

题面

  Bzoj2173

解析

  设$g_i$表示$\sum_j a_j = i$的权值和,$g_0=0$,$f_i$表示斐波那契数列第$i$项,$g_i = \sum_{j=1}^i g_{j}* f_{i-j}+f_i$

  写成生成函数:设$G(x)=\sum_{i=0}^{\infty}g_i x^i$,$F(x)=\sum_{i=0}^{\infty}f_i x^i$,那么有:$$G=G*F+F\\ G=\frac{F}{1-F}$$

  因为$f_i=f_{i-1}+f_{i-2}$,$f_0=0$,$f_1=1$,所以有:$$F=xF+x^2F+x\\F=\frac{x}{1-x-x^2}$$

  代入$G$的表达式:$$G=\frac{x}{1-2x-x^2}\\ G=2xG+x^2G+x$$

  即:$g_0=0$,$g_1=1$,$g_i=2g_{i-1}+g_{i-2}$

  欧拉定理+矩阵快速幂

  $O(\log mod)$

 代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn = 10005, mod = 1000000007;

int add(int x, int y)
{
    return x + y < mod? x + y: x + y - mod;
}

int n;
char s[maxn];

struct matrix{
    int a[2][2];
}dw0, dw1;

matrix operator * (matrix x, matrix y)
{
    matrix ret = dw0;
    for(int i = 0; i < 2; ++i)
        for(int j = 0; j < 2; ++j)
            for(int k = 0; k < 2; ++k)
                ret.a[i][j] = add(ret.a[i][j], 1LL * x.a[i][k] * y.a[k][j] % mod);
    return ret;
}

matrix mat_qpow(matrix x, int y)
{
    matrix ret = dw1;
    while(y)
    {
        if(y&1)
            ret = ret * x;
        x = x * x;
        y >>= 1;
    }
    return ret;
}

int main()
{
    scanf("%s", s + 1);
    int m = strlen(s + 1);
    for(int i = 1; i <= m; ++i)
        n = (10LL * n + s[i] - '0') % (mod - 1);
    dw1.a[0][0] = dw1.a[1][1] = 1;
    matrix A = dw0, ans = dw0;
    ans.a[0][1] = A.a[0][1] = A.a[1][0] = 1;
    A.a[1][1] = 2;
    ans = ans * mat_qpow(A, n);
    printf("%d", ans.a[0][0]);
    return 0;
}
View Code
posted @ 2020-04-06 17:02  Mr_Joker  阅读(279)  评论(0编辑  收藏  举报