CF172B Pseudorandom Sequence Period – 题解

ZhanPJ 发布于 2022-10-20 44 次阅读


题目传送门:CF172B

题目大意

给出随机数生成函数,求得其循环节长度。
随机函数:

while(1){//修改成了 C++ 版本。
  x=(a*x+b)%m;
  printf("%d",x);
}

解题思路

我们发现数字会取余 m,所以生成的数字在 0m 的区间内。而我们知道循环节的出现一定由一个已经出现过的数字为开始(证明见后)。
所以我们做一个数组 visvis_i 来存储数字 i 第一次出现的位置 id。如果 vis_i0,证明 i 没有出现过,不管。如果不为 0,那么 i 就出现过了,输出本次的 id 减去上次的 id 即可。

证明

在输入过 ab 之后,ab 已经成定值,而 m 仅限制取值区间。
所以,对于任意 xa \times x + b 的值是固定的。那么取余 m 的结果也是定值。
因为结果的值可以确定,所以出现相同数字就代表有循环节出现。

代码

#include <iostream>
#include <cstdio>
using namespace std;
int vis[100005];
int main(){
    int a,m,b,x,i=1;
    scanf("%d%d%d%d",&a,&b,&m,&x);
    vis[x]=1; 
    while(1){
        x=(a*x+b)%m;//题目给的代码
        i++;
        if(vis[x])return !printf("%d",i-vis[x]);
        vis[x]=i;
    }
    //因为取余 m,如果有可能 0 到 m-1 都跑了一遍,它必定会出现循环节。
    //所以使用 return !printf("%d",i-vis[x]); 即可。
}