【codejam2008_Round1A_C】Numbers(矩阵快速幂)

题目大意:
计算 (3+5)n ( 3 + 5 ) n 整数部分末三位。

题解:
题解原网址https://code.google.com/codejam/contest/32016/dashboard#s=a&a=2

α=3+5 α = 3 + 5 β=35 β = 3 − 5 Xn=αn+βn X n = α n + β n
可证明 Xn X n 为整数(二项式定理)

Xn=αn+βn=i=0n(ni)3ni(5)i+i=0n(ni)3ni5i=2(i=0n/2(n2i)3n2i5i) X n = α n + β n = ∑ i = 0 n ( i n ) 3 n − i ( − 5 ) i + ∑ i = 0 n ( i n ) 3 n − i 5 i = 2 ( ∑ i = 0 ⌊ n / 2 ⌋ ( 2 i n ) 3 n − 2 i 5 i )

0<βn<1 ∵ 0 < β n < 1
αn α n 的整数部分一定等于 Xn1 X n − 1

Solution1:(共轭)

αn=an+bn5 α n = a n + b n 5 βn=anbn5 β n = a n − b n 5
Xn=2an ∴ X n = 2 a n
αn+1=(3+5)(an+bn5)=(3an+5bn)+(an+3bn)5 ∵ α n + 1 = ( 3 + 5 ) ( a n + b n 5 ) = ( 3 a n + 5 b n ) + ( a n + 3 b n ) 5
an+1=3an+5bn ∴ a n + 1 = 3 a n + 5 b n bn+1=an+3bn b n + 1 = a n + 3 b n

(anbn)=A(an1bn1)=An(a0b0),A=(3153) ( a n b n ) = A ( a n − 1 b n − 1 ) = A n ( a 0 b 0 ) , A = ( 3 5 1 3 )

初始状态: α0=1 α 0 = 1
(a0b0)=(10) ( a 0 b 0 ) = ( 1 0 )

可使用矩阵快速幂,时间复杂度 O(logn) O ( l o g n )

方法2:

α+β=6 ∵ α + β = 6 αβ=4 α β = 4
所以α和β是 x26x+4=0 x 2 − 6 x + 4 = 0 的两个根
α2=6α4 ∴ α 2 = 6 α − 4 β2=6β4 β 2 = 6 β − 4
αn+2=6αn+14αn ∴ α n + 2 = 6 α n + 1 − 4 α n βn+2=6βn+14βn β n + 2 = 6 β n + 1 − 4 β n
两式相加,得:

Xn+2=6Xn+14Xn X n + 2 = 6 X n + 1 − 4 X n

所以可使用矩阵乘法:
(Xn+1Xn)=B(XnXn1)=Bn(X1X0),B=(6140) ( X n + 1 X n ) = B ( X n X n − 1 ) = B n ( X 1 X 0 ) , B = ( 6 − 4 1 0 )

方法3:(方法2存在循环,可优化)

我们只需要最后结果的最后3位,经过法2的多次计算后发现,结果从 n=4 n = 4 n=103 n = 103 是一个长度为100的循环,然后继续下去。
如果n很大,输出 (n3)mod100+3 ( n − 3 ) m o d 100 + 3

代码:

#include<cstdio>
#include<vector>
using namespace std;
typedef vector<int> Vector;
typedef vector<Vector> Matrix;
Matrix operator * (Matrix a,Matrix b)
{
    int as1=a.size(),as2=a[0].size(),bs1=b.size(),bs2=b[0].size();
    if(as2!=bs1)
        return Matrix(0,Vector(0));
    Matrix ret(as2,Vector(bs1,0));
    for(int i=0;i<as1;i++)
        for(int j=0;j<bs2;j++)
            for(int k=0;k<as2;k++)
                ret[i][j]=(ret[i][j]+(a[i][k]*b[k][j])%1000)%1000;
    return ret;
}
Matrix pow(Matrix a,int b)
{
    Matrix c(a.size(),Vector(a.size(),0));
    for(int i=0;i<(int)a.size();i++)
        c[i][i]=1;      
    for(;b;b>>=1,a=a*a)
        if(b&1)
            c=c*a;
    return c;
}
namespace SolutionA
{
    int solve(int n)
    {
        Matrix a(2,Vector(2,0));
        a[0][0]=3;a[0][1]=5;
        a[1][0]=1;a[1][1]=3;
        a=pow(a,n);
        return (a[0][0]*2+999)%1000;
    }
}
namespace SolutionB
{
    int solve(int n)
    {
        Matrix b(2,Vector(2));
        b[0][0]=6;b[0][1]=-4;
        b[1][0]=1;b[1][1]=0;
        b=pow(b,n);
        int ret=b[1][0]*6+b[1][1]*2+999;
        while(ret<0)
            ret+=1000;
        return ret%1000;
    }
}
namespace SolutionC
{
    int solve(int n)
    {
        if(n>103)
            n=(n-3)%100+3;
        return SolutionB::solve(n);
    }
}
int main()
{
    int T,n;
    scanf("%d",&T);
    for(int i=1;i<=T;i++)
    {
        scanf("%d",&n);
        //printf("Case #%d: %03d\n",i,SolutionA::solve(n));
        //printf("Case #%d: %03d\n",i,SolutionB::solve(n));
        printf("Case #%d: %03d\n",i,SolutionC::solve(n));
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值