P3306 [SDOI2013] 随机数生成器 题解
题解
思路
由题目中可知:
可以得出:
其中 \(n+1\) 即所求的天数。
对于后面的那部分,我们联想到平方差公式和立方差公式:
可以推广为:
于是,我们将上式两边同时乘以 \(a-1\):
观察到该式可以变形为 BSGS 的标准形式:
这个式子看起来十分复杂,所以让我们换元,方便下文叙述。预备——换
令 \(c=(a-1)x_1+b\),\(d=(a-1)t+b\)。
于是上式变为:
可以用 BSGS 求解。
注意求出来的 \(n\) 要加一。
细节
这是本题的重要部分。
首先,当 \(x_1=t\) 时,第一天就读到了第 \(t\) 页,直接输出 \(1\)。
其次,当 \(a=0\) 时,从第二天开始每天都读第 \(b\) 页。如果 \(b=t\) 那么输出 \(2\),否则输出 \(-1\)。
再其次,当 \(c\equiv0\pmod{p}\) 时,每天读的都是第 \(x_1\) 页。而既然能够走到这步,那么第一天就读不到第 \(t\) 页。因此输出 \(-1\)。
最后,当 \(a=1\) 时,代入式子可得:
使用 exgcd 求解。
关于求 \(c\) 的逆元,由于 \(p\) 是质数,且经过上述分类讨论,\(c\not\equiv0\pmod{p}\),因此 \(c\) 与 \(p\) 一定互质。可以使用快速幂法和 exgcd 求解。
既然我们在讨论 \(a=1\) 时使用了 exgcd,那么使用 exgcd 更好。但是笔者一开始没讨论 \(\sout{a=1}\),所以用的快速幂。
为什么 markdown 的分割线对 \(\sout{\LaTeX}\) 不生效
实现
上面说得差不多了。注意中间量开long long
。
代码
快速幂和 exgcd:
int qpow(int x,int y,int p) { int ret=1; while(y) { if(y&1) ret=1ll*ret*x%p; x=1ll*x*x%p,y>>=1; } return ret; } int exgcd(int p,int q,int &x,int &y) { if(!q) { x=1,y=0; return p; } int ret=exgcd(q,p%q,y,x); y-=(p/q)*x; return ret; }
BSGS:
std::unordered_map<int,int> mp; int BSGS(int a,int s,int p) { mp.clear(); int len=ceil(sqrt(p)),tmp=1; for(int i=0;i<len;i++) { mp[1ll*tmp*s%p]=i; tmp=1ll*tmp*a%p; } int base=1; for(int i=0;i<=len;i++) { if(mp.find(base)!=mp.end()) if(1ll*i*len-mp[base]>=0) return 1ll*i*len-mp[base]; base=1ll*base*tmp%p; } return -2; //因为要+1 }
注意多测。
主体部分:
int p,a,b,x,t; scanf("%d%d%d%d%d",&p,&a,&b,&x,&t); if(x==t) { printf("1\n"); continue; } if(a==0) { printf("%d\n",b==t?2:-1); continue; } if(a==1) { int tmp=t-x; int tx,ty; int gcd=exgcd(b,p,tx,ty); if(tmp%gcd) printf("-1\n"); else printf("%d\n",(1ll*tx*tmp/gcd%p+p)%p+1); continue; } int c=(1ll*(a-1)*x+b)%p,d=(1ll*(a-1)*t+b)%p; if(!c) { printf("-1\n"); continue; } d=1ll*d*qpow(c,p-2,p)%p; printf("%d\n",BSGS(a,d,p)+1);
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】