【NOI2012】随机数生成器
Description
栋栋最近迷上了随机算法,而随机数生成是随机算法的基础。栋栋准备使用线性同余法(Linear Congruential Method)来生成一个随机数列,这种方法需要设置四个非负整数参数 m, a, c, X0,按照下面的公式生成出一系列随机数< Xn >:
Xn+1=(aXn+c) mod m
其中 mod m 表示前面的数除以m的余数。从这个式子可以看出,这个序列的下一个数总是由上一个数生成的。
用这种方法生成的序列具有随机序列的性质,因此这种方法被广泛地使用,包括常用的 C++和 Pascal 的产生随机数的库函数使用的也是这种方法。
栋栋知道这样产生的序列具有良好的随机性,不过心急的他仍然想尽快知道Xn是多少。由于栋栋需要的随机数是0, 1, … ,n−1 之间的,他需要将 Xn 除以g取余得到他想要的数,即 Xn mod g,你只需要告诉栋栋他想要的数Xn mod g是多少就可以了。
Solution
一般一维递推可以用矩阵乘法解决。
然而这题m的规模达到 1018 ,两个不大于 1018 的数相乘不能接受。
那么把乘法改成加法:例如
a×b
就是
b
个
Code
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define fo(i,j,k) for(int i=j;i<=k;i++)
#define ll long long
using namespace std;
ll b[2][2],a[2],c[2][2];
ll m;
ll q[2][2]={{1,0},{0,1}};
ll mul(ll x,ll y)
{
ll b=0;
while(y)
{
if(y%2) b=(b+x)%m;
y/=2;
x=x*2%m;
}
return b;
}
void mul1()
{
memset(c,0,sizeof(c));
fo(i,0,1)
fo(j,0,1)
fo(k,0,1) c[i][j]=(c[i][j]+mul(q[i][k],b[k][j]))%m;
memcpy(q,c,sizeof(c));
}
void mul2()
{
memset(c,0,sizeof(c));
fo(i,0,1)
fo(j,0,1)
fo(k,0,1) c[i][j]=(c[i][j]+mul(b[i][k],b[k][j]))%m;
memcpy(b,c,sizeof(c));
}
void pow(ll n)
{
while(n)
{
if(n%2) mul1();
n/=2;
mul2();
}
}
int main()
{
ll n,g;
scanf("%lld %lld %lld %lld %lld %lld",&m,&b[0][0],&a[1],&a[0],&n,&g);
b[1][0]=b[1][1]=1;
pow(n);
ll ans=0;
fo(i,0,1)
ans=(ans+mul(a[i],q[i][0]))%m;
printf("%lld",ans%g);
}