传送门
题解
思路
由题目中可知:
可以得出:
其中 \(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");elseprintf("%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);