九度OJ 1081:递推数列 (递归,二分法)

时间限制:1 秒

内存限制:32 兆

特殊判题:

提交:6194

解决:864

题目描述:

给定a0,a1,以及an=p*a(n-1) + q*a(n-2)中的p,q。这里n >= 2。 求第k个数对10000的模。

输入:

输入包括5个整数:a0、a1、p、q、k。

输出:

第k个数a(k)对10000的模。

样例输入:
20 1 1 14 5
样例输出:
8359
来源:
2009年清华大学计算机研究生机试真题

思路:

直接一步一步的递推肯定是要超时的。对这种求第n个数的递推题,有logn的解法。

基本思想是a(n)由a(n/2)得到,逐次循环。

an=p*a(n-1) + q*a(n-2)

可以得到an=p2*a(n-2) + q2*a(n-4)

其中 p2 = (p*p+2*q), q2 = -q*q

这种思想是典型二分法,此题重点是学到了一种解题思想。


代码:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
 
int main(void)
{
    int p, q, p2, q2, k;
    long long a0, a1, a2, a3;
 
    while (scanf("%lld%lld%d%d%d", &a0, &a1, &p, &q, &k) != EOF)
    {
        if (k == 0)
        {
            printf("%lld\n", a0);
            continue;
        }
        a0 %= 10000;
        a1 %= 10000;
        p %= 10000;
        q %= 10000;
        while (k>1)
        {
            a2 = p*a1+q*a0;
            while (a2<0)
                a2 += 1000000000;
            a2 %= 10000;
            if (k%2 == 1)
            {
                a3 = p*a2+q*a1;
                while (a3<0)
                    a3 += 1000000000;
                a3 %= 10000;
                a0 = a1;
                a1 = a3;
            }
            else
                a1 = a2;
            p2 = (p*p+2*q)%10000;
            q2 = -q*q;
            while (q2<0)
                q2 += 1000000000;
            q2 %= 10000;
            p = p2;
            q = q2;
            k /= 2;
        }
        printf("%lld\n", a1);
    }
 
    return 0;
}
/**************************************************************
    Problem: 1081
    User: liangrx06
    Language: C
    Result: Accepted
    Time:10 ms
    Memory:912 kb
****************************************************************/


别人的代码,用了另一种递归方式,思想是类似的:

#include<stdio.h>
#define MOD 10000
long long a0,a1,p,q,k;
void matrixpow(long long *data,long long k)
{
        long long t1,t2,t3,t4;        
        long long d1,d2,d3,d4;
        d1 = p;
        d2 = q;
        d3 = 1;
        d4 = 0;
        if(k == 1 || k == 0)
                return;        
        matrixpow(data,k/2);
        t1 = (data[0]*data[0]+data[1]*data[2])%MOD;
        t2 = (data[0]*data[1]+data[1]*data[3])%MOD;
        t3 = (data[0]*data[2]+data[2]*data[3])%MOD;
        t4 = (data[1]*data[2]+data[3]*data[3])%MOD;
        data[0] = t1;
        data[1] = t2;
        data[2] = t3;
        data[3] = t4;
        if(k&1)
        {
                t1 = (data[0]*d1+data[1]*d3)%MOD;
                t2 = (data[0]*d2+data[1]*d4)%MOD;
                t3 = (data[2]*d1+data[3]*d3)%MOD;
                t4 = (data[2]*d2+data[3]*d4)%MOD;
                data[0] = t1;
                data[1] = t2;
                data[2] = t3;
                data[3] = t4;
        }
}
void main()
{        
        long long data[4];
        long long res;
        while(scanf("%lld%lld%lld%lld%lld",&a0,&a1,&p,&q,&k)!=EOF)
        {
                data[0] = p;
                data[1] = q;
                data[2] = 1;
                data[3] = 0;
                matrixpow(data,k-2);
                if(k == 0)
                        res = a0%MOD;
                else
                {
                        if(k == 1)
                                res = a1%MOD;
                        else
                        {
                                if(k>2)
                                        res = (data[0]*p*a1+a1*q*data[2]+a0*p*data[1]+a0*q*data[3])%MOD;
                                else
                                        res = (a1*p+a0*q)%MOD;
                        }
                }
                printf("%lld\n",res);
        }
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值