【Java】521公式

7 篇文章 0 订阅

在暑假集训的三校联赛上遇到的题目,
原题网址:http://www.acmusc.cn:9988/problem/246/

原题如下:


521公式

描述

现有公式:F(n) = 5*F(n-1) + 2*F(n-2) +F(n-3) 当n>=3时;因为它的系数分别是5 2 1,所以我们叫它521公式。已知F(0)=1,F(1)=3,F(2)=5。

我们想要知道521公式的区间和模2017的值,也就是说,给定l和r(l<=r),我们要求F(l) + F(l+1) + F(l+2) + … + F(r)取模2017的值。

输入

第一行一个数T,代表有多少组数据。接下来T行,每一行2个数,分别是l和r,输入保证0 <= l <= r <= 1e9。

输出

输出答案取模2017的值,按照”Case %d: “的格式输出。

样例输入

3
0 2
2 5
123 234

样例输出
Case 1: 9
Case 2: 1144
Case 3: 1979

根据该数列的递推公式F(n) = 5*F(n-1) + 2*F(n-2) +F(n-3)不难看出其中一定位置后的元素的数值是巨大的,题目中也提到结果需对2017取余,于是可以得知,题目要求的数列会是一个循环数列,所以这道题可以利用这点进行解答

求循环节代码:

public static void main(String[] args){
        Scanner sc = new Scanner(System.in);
            int a[] = new int [10000005];//目标数列
            a[0] = 1;
            a[1] = 3;
            a[2] = 5;
            for(int i = 3;i <= 10000000;i++){
                a[i] = (5*a[i-1] + 2*a[i-2] + 1*a[i-3]) % 2017;
                if(a[i] == 5 && a[i-1] == 3 && a[i-2] == 1)
                    System.out.println(a[i-2] + " " + (i-2));//检测到进入循环的位置时输出该数值及项数
            }
        }

得出结果为:

1 1356769
1 2713538
1 4070307
1 5427076
1 6783845
1 8140614
1 9497383

可以看出,每组结果的项数差均为:1356769,即循环节
对上面代码稍作处理,可求得一次循环元素值总和对2017取余后为0,到这里,问题就解决了大部分了

对问题提交的代码如下:

import java.util.Scanner;

public class Main{

    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);
        long a[] = new long [1356770];
        a[0] = 1;
        a[1] = 3;
        a[2] = 5;
        for(int i = 3;i <= 1356769;i++){
            a[i] = (5*a[i-1] + 2*a[i-2] + a[i-3])%2017;
        }
        int t = sc.nextInt();
        int count = 1;//测试数据的序号
        while(t-- > 0){
            int s = sc.nextInt();//区间起点
            int e = sc.nextInt();//区间终点
            long sum = 0;//总和
            if((e-s) >= 1356768){
                e = (e-s) % 1356769;
                s = s % 1356769;
                e += s;
            }//当区间长度超过一次循环时进行消除
            s = s % 1356769;
            e = e % 1356769;
            //将起始位置与终止位置保证在0-1356768间
            if((e-s) < 1356768)
                for(int i = s;i <= e;i++)
                    sum = (sum + a[i]) % 2017;
                System.out.println("Case "+count+": "+sum);
            ++count;
        }
        }

    }

找循环节的方法可以适用于一个由不变的公式推算出的并对结果进行了取余的递推数列

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值