PE 440 【二阶递推】

本文探讨了一种特殊的木块拼接问题,通过数学推导得出递推式及通项公式,并利用矩阵快速幂等技巧高效求解,最终给出一个具体的算法实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

被拖进的坑,555
题目大意:有10种长度为1的木块,1种长度为2的木块,设T(n)表示长度为n的木板拼凑方案数,求gcd(T(ca),T(cb))mod987898789(1<=a,b,c<=2000)

容易得到递推式 T(n)=10T(n1)+T(n2),首项 T(1)=10,T(2)=101
和Fibonacci数列相似,我们可以推出它一些相关的性质

T(n)的通项公式:T(n)=(5+26)n+1(526)n+1226
可得 T(a+b)=T(a)T(b)+T(a1)T(b1)

1) gcd(Tn,Tn1)==1
证明 : gcd(Tn,Tn1)=gcd(10Tn1+Tn2,Tn1)=gcd(Tn2,Tn1)=gcd(T2,T1)=1

2) gcd(Ta,Tb)==Tgcd(a+1,b+1)1
证明:gcd(Ta,Tb)=gcd(TabTb+Tab1Tb1,Tb)=gcd(Tab1Tb1,Tb)=gcd(T(a+1)(b+1)1,T(b+1)1)=Tgcd(a+1,b+1)1

所以可得gcd(T(ca),T(cb))=T(gcd(ca+1,cb+1)1)
g=gcd(a,b)
gcd(ca+1,cb+1)=cg+1c(mod2)+1agandbgareoddotherwise

证明:
gcd(ca+1,cb+1)=gcd(cacb,cb+1)=gcd(cab1,cb+1)=gcd(cab+cb,cb+1)=gcd(c|a2b|+1,cb+1)

i) 最终 a = b = g,值为 cg+1
    若agbg为偶数那么一定可以再分
ii) 出现 a 或 b == 0,值为 c mod 2 + 1

现在就可以考虑枚举 a,b,这样需要先预处理出sum[c]=gcd(T(ca),T(cb))

但是 ca 很大,所以还要找到Tmod987898789的循环节
循环节长度为987898788

//PS:这里并没有形成 ρ 环,蒟蒻表示这里还搞得不是很清楚。。。

所以求T(ca)的时候还要加上矩乘。。。

复杂度O(N2logN),(其实还有个23,就算做常数啦。。。

【答案】970746056

#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cstdio>
#include<string>
#include<cstring>
#include<ctime>
#include<cmath>
#define N 2000
#define M 987898789
#define LL long long
using namespace std;

LL ans;
LL sum[N+5];
LL A[2][2],B[2][2],t[2][2];

LL ksm(LL a,LL b,LL c)
{
    LL ret = 1;
    for (;b;b >>= 1,a = a * a % c)
        if (b & 1) ret = ret * a % c;
    return ret;
}

LL mul(LL a[][2],LL b[][2])
{
    for (int i = 0;i < 2;i ++)
        for (int j = 0;j < 2;j ++)
        {
            t[i][j] = 0;
            for (int k = 0;k < 2;k ++)
                (t[i][j] += a[i][k] * b[k][j]) %= M;
        }
    for (int i = 0;i < 2;i ++)
        for (int j = 0;j < 2;j ++)
            a[i][j] = t[i][j];
}

LL matrix_ksm(LL b)
{
    A[0][0] = 10,A[0][1] = A[1][0] = 1,A[1][1] = 0;
    B[0][0] = B[1][1] = 1,B[0][1] = B[1][0] = 0;
    for (;b;b >>= 1,mul(A,A))
        if (b & 1) mul(B,A);
    return B[0][0];
}

int gcd(int a,int b)
{
    return b ? gcd(b,a % b) : a;
}

int main()
{
    for (int i = 1;i <= N;i ++)
        for (int j = 1;j <= N;j ++)
            (sum[i] += matrix_ksm(ksm(j,i,M - 1))) %= M;
    for (int a = 1;a <= N;a ++)
        for (int b = 1;b <= N;b ++)
        {
            int g = gcd(a,b);
            if (((a / g) & 1) && ((b / g) & 1)) (ans += sum[g]) %= M;
                else (ans += 11000) %= M;
        }
    cout << ans << endl;

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值