P3306 [SDOI2013] 随机数生成器 题解

传送门

题解

思路

由题目中可知:

\[\large x_i \equiv ax_{i-1}+b\pmod{p} \]

可以得出:

\[\large t=x_{n+1} \equiv a^nx_1+b \sum_{i=0}^{n-1}a^i\pmod{p} \]

其中 \(n+1\) 即所求的天数。
对于后面的那部分,我们联想到平方差公式和立方差公式:

\[\large a^2-1=(a-1)(a+1) \\ a^3-1=(a-1)(a^2+a+1) \]

可以推广为:

\[\large a^n-1=(a-1)\sum_{i=0}^{n-1}a^i \]

于是,我们将上式两边同时乘以 \(a-1\)

\[\large (a-1)t\equiv (a-1)x_1a^n+b(a^n-1)\pmod{p} \]

观察到该式可以变形为 BSGS 的标准形式:

\[\large a^n\equiv[(a-1)t+b][(a-1)x_1+b]^{-1}\pmod{p} \]

这个式子看起来十分复杂,所以让我们换元,方便下文叙述。预备——换
\(c=(a-1)x_1+b\)\(d=(a-1)t+b\)
于是上式变为:

\[\large a^n\equiv c^{-1}d\pmod{p} \]

可以用 BSGS 求解。
注意求出来的 \(n\) 要加一。

细节

这是本题的重要部分。
首先,当 \(x_1=t\) 时,第一天就读到了第 \(t\) 页,直接输出 \(1\)
其次,当 \(a=0\) 时,从第二天开始每天都读第 \(b\) 页。如果 \(b=t\) 那么输出 \(2\),否则输出 \(-1\)
再其次,当 \(c\equiv0\pmod{p}\) 时,每天读的都是第 \(x_1\) 页。而既然能够走到这步,那么第一天就读不到第 \(t\) 页。因此输出 \(-1\)
最后,当 \(a=1\) 时,代入式子可得:

\[\large t\equiv x_1+bn\pmod{p} \]

使用 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);

\[\Huge End \]

posted @   整齐的艾萨克  阅读(4)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示