数列(循环节)

题意

有一个整数数列a0,a1,a2,a3

该数列的前两项a0,a1的具体值已知,其它项可以通过如下递推式求出:

  • an=p×an1+q×an2(n2)

给定一个可能非常大的正整数N和两个不太大的正整数M,K,请你分别计算并输出aN+1,aN+2,,aN+KM取模的值。

题目链接:https://www.acwing.com/problem/content/4584/

数据范围

0<a0,a1,p,q32693
0<M3×103
0<K104
0<N<10106

思路

首先我们观察到M的范围较小,只有3×103,因此数列中的数最多有3×103个取值。

N特别大,这种题目的典型套路是分析循环节。

那么什么时候才会出现循环呢?如果ai1ai在之前出现过,即存在j<i满足aj1=ai1并且aj=ai。那么就出现循环了。其中循环节的长度为r=ij

因此,我们只需要计算出N位于循环节的哪个位置即可,即:(n(j1))%r=n%r(j1)%r

代码

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>

using namespace std;

const int N = 1e7 + 10, M = 3010;

int a[N], p, q;
int m, k;
int id[M][M];
string n;

int main()
{
    cin >> a[0] >> a[1] >> p >> q >> m >> k >> n;
    a[0] %= m, a[1] %= m;
    id[a[0]][a[1]] = 1;
    int ed, st;
    for(int i = 2; i < N; i ++) {
        a[i] = (a[i - 1] * p % m + a[i - 2] * q % m) % m;
    }
    for(int i = 2; i < N; i ++) {
        if(id[a[i - 1]][a[i]]) {
            ed = i - 2;
            st = id[a[i - 1]][a[i]] - 1;
            break;
        }
        id[a[i - 1]][a[i]] = i;
    }
    int len = ed - st + 1;
    if(n.size() <= 7) {
        int num = 0;
        for(int i = 0; i < n.size(); i ++) {
            num = num * 10 + n[i] - '0';
        }
        for(int i = 1; i <= k; i ++) {
            cout << a[num + i] << '\n';
        }
    }
    else {
        int num = 0;
        for(int i = 0; i < n.size(); i ++) {
            num = (num * 10 % len + n[i] - '0') % len;
        }
        int r = (num - st % len + len) % len;
        for(int i = st + r + 1; i <= st + r + k; i ++) {
            cout << a[i] << '\n';
        }
    }
    return 0;
}
posted @   pbc的成长之路  阅读(150)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
点击右上角即可分享
微信分享提示