【JZOJ3211】【SDOI2013】隨機數生成器

╰( ̄▽ ̄)╭

小 W喜歡讀 書,尤其喜歡讀 書,尤其喜歡讀《約翰克里斯 朵夫》。 最近小 W準備讀一本新書,這本一共有 p頁, 頁碼範圍爲 0..p -1。
小 W很忙,所以每天只能讀一頁書 。爲了使事情有趣一些 ,他打算使用 NOI2012上學習的線性同餘法生成 一個序列 ,來決定每天具體讀哪一頁 。
我們用 Xi來表示通過這種方法生成出來第 i個數 ,也即小 W第 i天會讀 哪一頁 。這個方法 需要設置 3個參數 a,b,X1,滿足 0≤a,b,X1≤p-1,且 a, b,X1都是整數 。按照下面的公式 按照下面的公式生成出來一系列的 整數。
Xi+1=(aXi+b)modp
其中 mod p 表示前面的數除以 p的餘數。
可以發現,這個序列中下一個數總是由上一個數生成的 ,而且每一項都在 0..p -1這個範圍內 ,是一個合法的頁碼。 同時需要注意 ,這種方法有可能導致某兩天讀的頁碼完全一樣 。
小 W非常急切 地想去讀這本書的第t頁。所以他想知道, 對於一組給定的 a, b,X1,如果使用線性同餘法來生成每一天讀的頁碼, 最早讀到第t頁是在哪一天,或者指出他永遠不會讀到第t頁。
p<=109

(⊙ ▽ ⊙)

容易求出x 的通項公式,
c=ba1

xn=an1(x1+c)c

特殊情況的處理:
在此之前我們必須保證a!=1
所以我們對於a=1 的情況利用擴展歐幾里得求解。
同時我們也得保證a!=0 ,因爲當a=0 並且n=1 時,00 是無意義的。
於是對於a=0 的情況直接特判求解。


此時,題目要求的是:xnt(mod p) ,等價於:an1(x1+c)ct(mod p)
移項可得:an1t+cx1+c(mod p)


特殊情況的處理:
在此之前,我們還要保證x1b 不同時爲0 ,否則會使得x1+c 這個分母爲0
所以對於x1b 同時爲0 的情況特判求解。


ans=n1 , S=t+cx1+c ,最終題目轉化爲求

aansS(mod p)

的最小正整數解。(關於ans

於是就能套大步小步算法(BSGS algorithm)了。

( ̄~ ̄)

#include<iostream>
#include<algorithm>
#include<stdio.h>
#include<string.h>
#include<math.h>
#define ll long long
using namespace std;
const char* fin="jzoj3211.in";
const char* fout="jzoj3211.out";
const ll inf=0x7fffffff;
const ll maxh=1000007;
ll t,i,j,k,A,B,X1,n,mo;
ll h[maxh],minx[maxh];
bool noans;
ll hash(ll v){
    ll k=v%maxh;
    while (h[k] && h[k]!=v) k=(k+1)%maxh;
    return k;
}
ll exgcd(ll a,ll b,ll &x,ll &y){
    if (b==0){
        x=1;
        y=0;
        return a;
    }
    ll r=exgcd(b,a%b,x,y);
    ll t=x;
    x=y;
    y=t-a/b*y;
    return r;
}
ll qpower(ll a,ll b){
    ll c=1;
    while (b){
        if (b&1) c=c*a%mo;
        a=a*a%mo;
        b>>=1;
    }
    return c;
}
int main(){
    freopen(fin,"r",stdin);
    freopen(fout,"w",stdout);
    scanf("%lld",&t);
    while (t--){
        scanf("%lld%lld%lld%lld%lld",&mo,&A,&B,&X1,&n);
        if (A==0){
            if (X1==n) printf("1\n");
            else if (B==n) printf("2\n");
            else printf("-1\n");
        }else if (A==1){
            ll x,y;
            k=exgcd(mo,B,x,y);
            if ((n-X1)%k) printf("-1\n");
            else{
                x*=(n-X1)/k;
                y*=(n-X1)/k;
                y=(y%(mo/k)+(mo/k))%(mo/k);
                printf("%lld\n",y+1);
            }
        }else if (B==0 && X1==0){
            if (X1==n) printf("1\n");
            else printf("-1\n");
        }else{
            ll ks=(ll)sqrt(mo),tmp=qpower(A,ks),tmd=qpower(tmp,mo-2);
            memset(h,0,sizeof(h));
            memset(minx,0,sizeof(minx));
            j=1;
            for (i=0;i<ks;i++){
                k=hash(j);
                h[k]=j;
                if (!minx[k] || minx[k]>i) minx[k]=i;
                j=j*A%mo;
            }
            ll C=B*qpower(A-1,mo-2)%mo;
            j=(n+C)*qpower(X1+C,mo-2)%mo;
            noans=true;
            for (i=0;i<=ks;i++){
                k=hash(j);
                if (h[k]){
                    ll ans=minx[k]+i*ks+1;
                    printf("%lld\n",ans);
                    noans=false;
                    break;
                }
                j=j*tmd%mo;
            }
            if (noans) printf("-1\n");
        }
    }
    return 0;
}

(⊙v⊙)

對於一個有遞推式的問題,可以考慮先求出通項公式,在對其因式分解。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章