BZOJ 3122 SDOI2013 随机数生成器

一大堆边界一开始并不知道,胡乱判了几个之后一直WA

无奈之下只好去下载了数据,然后就疯狂判各种奇怪的边界了

刨去边界问题

首先我们考虑a=1的情况

x1+k*b=t(mod p)

ex_gcd即可解

考虑a>1的情况

令S=X+b/(a-1)

原式就变成了一个等比数列

即S1*a^k=(t+b/(a-1))(mod p)

移项之后BSGS解即可

其他边界都可以O(1)判断

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<map>
#include<cmath>
using namespace std;

typedef long long LL;
int T;
LL p,a,b,x1,x,y,d,t,inv;
map<LL,int>Q;
void ex_gcd(LL a,LL b,LL &d,LL &x,LL &y){
    if(b==0){x=1;y=0;d=a;return;}
    ex_gcd(b,a%b,d,y,x);y-=(a/b)*x;
}
LL pow_mod(LL v,LL b){
    LL tmp=1;
    while(b){
        if(b&1)tmp=tmp*v%p;
        v=v*v%p;b>>=1;
    }return tmp;
}
LL log_mod(LL a,LL b,LL n){
    if(a==0&&b==0)return 1;
    if(a==0)return -1;
    LL m=(int)(sqrt(n+0.5))+1;Q.clear();
    LL v=pow_mod(a,m);v=pow_mod(v,n-2);
    LL e=1;Q[1]=0;
    for(int i=1;i<m;++i){
        e=e*a%n;
        if(!Q.count(e))Q[e]=i;
    }
    for(int i=0;i<m;++i){
        if(Q.count(b))return i*m+Q[b]+1;
        b=b*v%n;
    }return -1;
}

int main(){
    scanf("%d",&T);
    while(T--){
        scanf("%lld%lld%lld%lld%lld",&p,&a,&b,&x1,&t);
        if(x1==t){printf("1\n");continue;}
        if(a==0){
            if(b==t){printf("2\n");continue;}
            else {printf("-1\n");continue;}
        }
        if(a==1){
            if(b==0){printf("-1\n");continue;}
            ex_gcd(b,p,d,x,y);
            x1=(t-x1+p)%p;
            if(x1%d!=0){printf("-1\n");continue;}
            x=x*x1;
            x=(x%p+p)%p;
            printf("%lld\n",x+1);
            continue;
        }
        inv=pow_mod(a-1,p-2);
        b=b*inv%p;
        x1=(x1+b)%p;t=(t+b)%p;
        x1=pow_mod(x1,p-2);
        t=t*x1%p;
        printf("%lld\n",log_mod(a,t,p));
    }return 0;
}

  

时间: 2024-12-24 05:38:48

BZOJ 3122 SDOI2013 随机数生成器的相关文章

BZOJ 3122 [Sdoi2013]随机数生成器 BSGS

题意:链接 方法: BSGS 解析: 首先他给出了你数列在mod p意义下的递推式. 所以我们可以求出来通项. Xn+1+k=a?(Xn+k) 所以b=(a?1)?k 则我们可以解出来k 那么这个数列的通项是什么呢? Xn=an?1?(X1+k)?k 题中给定Xn 求出n就行了. 所以只需要移项就好了. 这里有个问题,此时我们的通项公式是不包含首项的,所以需要特判首项,另外还有第一项以外为常数项的时候. 代码: #include <cmath> #include <cstdio>

BZOJ 3122 SDOI2013 随机数生成器 数论 EXBSGS

题目大意:给定一个数列X(i+1)=(a*Xi+b)%p 求最小的i>0,使Xi=t 0.0 此题能1A真是太好了 首先讨论特殊情况 若X1=t ans=1 若a=0 ans=b==t?2:-1 若a=1 X1+b*(ans-1)==t (%p) 扩展欧几里得 令 temp=b/(a-1) 则有 (X(i+1)+temp)=a*(Xi+temp) Xans=(X1+temp)*a^(ans-1)-temp 其中Xans%p=t 则有 (X1+temp)*a^(ans-1)-temp == t (

【BZOJ 3122】 [Sdoi2013]随机数生成器 (BSGS)

3122: [Sdoi2013]随机数生成器 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 1442  Solved: 552 Description Input 输入含有多组数据,第一行一个正整数T,表示这个测试点内的数据组数. 接下来T行,每行有五个整数p,a,b,X1,t,表示一组数据.保证X1和t都是合法的页码. 注意:P一定为质数 Output 共T行,每行一个整数表示他最早读到第t页是哪一天.如果他永远不会读到第t页,输出-1. Sa

bzoj3122: [Sdoi2013]随机数生成器

3122: [Sdoi2013]随机数生成器 Description Input Output HINT $ 0 \leqslant a \leqslant P-1,0 \leqslant b \leqslant P-1,2 \leqslant P \leqslant 10^9 $ BSGS裸题 把线性递推式转化为通项公式为 $ x[n] = x1 * A^{n-1} + \frac {B*A^{n-1}} {A-1} = t $ 移项,得: $ (x1 + \frac {B} {A-1}) *

【bzoj3122】: [Sdoi2013]随机数生成器 数论-BSGS

[bzoj3122]: [Sdoi2013]随机数生成器 当a>=2 化简得 然后 BSGS 求解 其他的特判 : 当 x=t  n=1 当 a=1  当 a=0 判断b==t 1 /* http://www.cnblogs.com/karl07/ */ 2 #include <cstdlib> 3 #include <cstdio> 4 #include <cstring> 5 #include <cmath> 6 #include <map&

BZOJ 2875: [Noi2012]随机数生成器( 矩阵快速幂 )

矩阵快速幂...+快速乘就OK了 -------------------------------------------------------------------------------------- #include<bits/stdc++.h> using namespace std; typedef long long ll; ll MOD, a, c, x, n, g; ll MUL(ll a, ll b) { ll ans = 0; for(; b; b >>= 1

[bzoj 2875][noi2012]随机数生成器

传送门 Description 栋栋最近迷上了随机算法,而随机数是生成随机算法的基础.栋栋准备使用线性同余法(Linear Congruential Me thod)来生成一个随机数列,这种方法需要设置四个非负整数参数m,a,c,X[0],按照下面的公式生成出一系列随机 数X[n]X[n+1]=(aX[n]+c)mod m其中mod m表示前面的数除以m的余数.从这个式子可以看出,这个序列的下一个数 总是由上一个数生成的.用这种方法生成的序列具有随机序列的性质,因此这种方法被广泛地使用,包括常用

[Sdoi2013]随机数生成器(BSGS)

#include<map> #include<cmath> #include<cstdio> #include<iostream> #define ll long long using namespace std; inline int read(){ int x=0;char ch=getchar(); while(ch<'0'||ch>'9')ch=getchar(); while(ch>='0'&&ch<='9'

BZOJ 3671 NOI2014 随机数生成器 贪心+暴力

题目大意:.....有点长自己看吧 首先既然是排序后的序列字典序最小,那么一定要选尽量小的数字走 然后T是1~m*n的序列 所以不存在重复(一开始我居然把这个条件看漏了) 好的这题贪心 每次选择没有被标记的最小点,然后把左下方和右上方都标记掉(记得标记重复时break,不然就挂了) 注意5000*5000的数组开两个int就是极限了 开多了妥妥MLE 所以T数组记得重复利用 暴力跑了38秒 不过这题每一行能够选择的区域一定是连续的 可以对于每一行维护一个l和r 每次更新取最值即可 这个代码跑了2