PE 440 【二阶递推】

来源:互联网 发布:sql tools 编辑:程序博客网 时间:2024/06/01 22:57

被拖进的坑,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 longusing 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;}
0 0
原创粉丝点击