【NOI2012】随机数生成器

来源:互联网 发布:记录商品价格的软件 编辑:程序博客网 时间:2024/05/23 01:16

Description

给你一个式子

Xn+1=(aXn+c)modm

求第n项

Solution

用什么

发现n十分的大1018,又只给1000ms,只能用O(logn)的方法
有什么可以做呢?
很明显是矩阵乘法!!!

构造一个转移矩阵。

先要弄初始矩阵。
先设初始矩阵有一位Xn,首先需要乘,并不用多开一位。要从Xn1推过来,多开一位Xn1。然后还要加c,再多开一位c。
那么初始矩阵有3位,[Xn1][Xn][c]
那么很明显要从Xn推到Xn+1,那么很明显转移矩阵
{{0,0,0},
{1,a,0},
{0,1,1}
}

发现普通的乘法会爆long long,怎么办?

可以用快速乘,类似快速幂,速度比快速幂多一个log
比如说是a*b,我们设f(i)=a*i
那么把b拆一下,f(b)=f(b/2) * 2+a *(b mod 2)

ll qsc(ll x,ll y){    ll z=0;    if(y==0)return z;    z=qsc(x,y/2);    z=z*2%m;    if(y%2==1)z=(z+x)%m;    return z;}

其实还可以用黑科技
把这些数强制转成double再转回来

Code

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#include<cmath>#define fo(i,a,b) for(i=a;i<=b;i++)#define ll long longusing namespace std;ll i,j,k,l,t,n,m,ans,a,b,c,g,x0,x1;ll qsc(ll x,ll y){    ll z=0;    if(y==0)return z;    z=qsc(x,y/2);    z=z*2%m;    if(y%2==1)z=(z+x)%m;    return z;}struct node{    ll ju[3][3];    node friend operator *(node a,node b){        node c;        memset(c.ju,0,sizeof(c.ju));        fo(i,0,2){            fo(j,0,2){                fo(k,0,2){                    c.ju[i][j]=(c.ju[i][j]+qsc(a.ju[i][k],b.ju[k][j]))%m;                }            }        }        return c;    }}f,ber;void qsm(node x,ll y){    while(y!=0){        if(y&1==1)f=f*x;        x=x*x;        y=y/2;    }}int main(){    scanf("%lld%lld%lld%lld%lld%lld",&m,&a,&c,&x0,&n,&g);    ber.ju[0][0]=0;ber.ju[0][1]=0;ber.ju[0][2]=0;    ber.ju[1][0]=1;ber.ju[1][1]=a;ber.ju[1][2]=0;    ber.ju[2][0]=0;ber.ju[2][1]=1;ber.ju[2][2]=1;    x1=(qsc(a,x0)+c)%m;    f.ju[0][0]=x0,f.ju[0][1]=x1,f.ju[0][2]=c;    qsm(ber,n-1);    printf("%lld",f.ju[0][1]%g);}
1 0