超链接:数学合集
又名巴士公司,北上广深,拔山盖世……
感叹:中华汉字真是博大精深啊!
他可以干嘛捏?
解方程: a x ≡ b ( m o d p ) a^x\equiv b\ (mod\ p) ax≡b (mod p)的最小非负整数解,不过a和p是互质滴
首先,我来告诉大家一个一个神奇的事实:
如果这个方程有解,那么最小解一定在[0,p-1)里
想要说明这个事情我们需要引出一个东西:幂取模的循环节
相信大家的小学的时候一定(不,我没做过)做过这样一个问题:
3 233 的 个 位 是 什 么 ? 3^{233}的个位是什么? 3233的个位是什么?
然后老师会告诉你这样去处理:找循环节
3 1 ⇒ 个 位 是 3 3 2 ⇒ 个 位 是 9 3 3 ⇒ 个 位 是 7 3 4 ⇒ 个 位 是 1 3 5 ⇒ 个 位 是 3 3^1\Rightarrow 个位是3\\ 3^2\Rightarrow 个位是9\\ 3^3\Rightarrow 个位是7\\ 3^4\Rightarrow 个位是1\\ 3^5\Rightarrow 个位是3 31⇒个位是332⇒个位是933⇒个位是734⇒个位是135⇒个位是3
欧,有没有发现 3 5 3^5 35的个位就和 3 1 3^1 31的个位一样了,这就说明如果我再往下,那么就又是3971,3971,不停循环,每4个循环一次
然后我们用 233 m o d 4 = 1 233\ mod \ 4=1 233 mod 4=1,得到答案3,然后老师就会奖励你一颗小行星小星星
上面那个问题其实告诉了我们一个事情:
3 x ≡ b ( m o d 10 ) 3^x \equiv b(mod\ 10) 3x≡b(mod 10)
这个b的取值只有4个
那么我们能不能再把这个事情稍稍扩展一下,变成:
a x ≡ b ( m o d p ) a^x \equiv b(mod\ p) ax≡b(mod p)
会产生循环节捏?——当然是可以的,原因很简单:由于我们要看的是模p以后的值,所以我们根本不关心其他的东西,而 a x m o d p = a x − 1 m o d p ∗ x m o d p a^x \ mod\ p=a^{x-1}\ mod\ p*x\ mod\ p ax mod p=ax−1 mod p∗x mod p
So,当两次模完p以后值一样时,就产生循环节了
现在我们再来看一下我们最终要解决的问题:解 a x ≡ b ( m o d p ) a^x\equiv b\ (mod\ p) ax≡b (mod p)的最小非负整数解,a和p互质
由于左边那个东西会产生循环节,而我们要得到的也只要是x的最小非负整数解,那我们是不是只要考虑循环节里能取到的值那不能等于b,然后输出对应的x就好了
那么接下来我们就来考虑一下这个循环节最长会有多长?
我们可以用两个办法来思考这个问题:
讲到这里,一定有人会说:那就从0~p-1暴枚一遍,非常优秀!
我们不是野蛮人,我们要讲究效率,来把复杂度降到 O ( p ) O(\sqrt p) O(p),使用一个类似分块的想法(那是什么,戳)
令 t = ⌈ p ⌉ t=\left\lceil\sqrt p\right\rceil t=⌈p⌉
我们不妨设 x = i ∗ t − j x=i*t-j x=i∗t−j,那么i在[0,t]里,j在[0,t)里,这样一定可以覆盖所有情况,那么方程就是:
a i ∗ t − j ≡ b ( m o d p ) a^{i*t-j} \equiv b(mod\ p) ai∗t−j≡b(mod p)
于是就变成了这样:
( a t ) i ≡ b ∗ a j ( m o d p ) (a^t)^i \equiv b*a^j(mod\ p) (at)i≡b∗aj(mod p)
然后你就会发现右边的取值只有 ⌈ p ⌉ \left\lceil\sqrt p\right\rceil ⌈p⌉种,左边的取值也只有 ⌈ p ⌉ \left\lceil\sqrt p\right\rceil ⌈p⌉种,那我们就可以美滋滋地匹配了
先枚举j,把右边的所有情况扔到一个hash里(我们管这个叫Baby Steps),然后枚举i,算出左边的值,在hash表里寻找有没有(我们管这个叫Giant Steps),完事!
stl大法好
ll bsgs(ll a,ll b,ll p){//return -1 表示无解
if (b>=p) return -1;
map<ll,ll> mp;
mp.clear();
b%=p;
ll t=(ll)sqrt(p)+1;
for (ll j=0;j<t;j++){
ll v=b*pow(a,j,p)%p;
mp[v]=j;
}
a=pow(a,t,p);
if (a==0){
if (b==0) return 1;
else return -1;
}
for (ll i=0;i<=t;i++){
ll v=pow(a,i,p);
if (mp.find(v)!=mp.end()){
ll tmp=mp[v];
if (i*t-tmp>=0) return i*t-tmp;
}
}
return -1;
}
也就是问题变成a和p不互质了,那怎么办办捏?
那就把a和p变成互质的不就完了吗?
我们让 d = g c d ( a , p ) d=gcd(a,p) d=gcd(a,p)
我们把整个式子除掉d(如果在这个过程中d不整除b,那么除非b=1,否则就无解了),得到了:
a x d ≡ b d ( m o d p d ) \frac{a^x}{d}\equiv \frac{b}{d}(mod\ \frac{p}{d}) dax≡db(mod dp)
把一个 a x − 1 a^{x-1} ax−1拎出来:
a x − 1 ∗ a d ≡ b d ( m o d p d ) a^{x-1}*\frac{a}{d}\equiv \frac{b}{d}(mod\ \frac{p}{d}) ax−1∗da≡db(mod dp)
现在想象一下,如果我们把这个 a d \frac{a}{d} da除到等号右边的话(程序中先不要在这里移项),方程是不是就变回原来的格式了!
原来的 a x a^x ax变成了 a x − 1 a^{x-1} ax−1,p变成了 p d \frac{p}{d} dp
肯定有人会说了: a x − 1 a^{x-1} ax−1和 p d \frac{p}{d} dp也不一定是互质的呀!
不要紧,再重复上面的操作直到d是1呗,那么最这个方程会长这样:
a x − k ∗ a k ∏ d i ≡ b ∏ d i ( m o d p ∏ d i ) a^{x-k}*\frac{a^k}{\prod d_i}\equiv \frac{b}{\prod d_i}(mod\ \frac{p}{\prod d_i}) ax−k∗∏diak≡∏dib(mod ∏dip)
现在我们把 a k ∏ d i \frac{a^k}{\prod d_i} ∏diak移过去(这里要使用逆元了,不会的童鞋戳我)
方程就是原来的格式了,而且这个时候 a x − k a^{x-k} ax−k和 p ∏ d i \frac{p}{\prod d_i} ∏dip一定互质了
然后我们就可以美滋滋地使用BSGS了(这时求出来的是x-k,答案别忘了加k)!
但是在这之前,我们还要考虑答案在0~k之间的情况,k是log级的,暴力都没问题,不过这个部分是可以在上面除gcd的时候顺便做掉的
OK,完事
ll ex_bsgs(ll a,ll b,ll p){
if (b==1) return 0;
ll d=gcd(a,p),k=0,s=1;
while(d>1){
if (b%d!=0) return -1;
k++;
b/=d;
p/=d;
s=(s*a/d)%p;
if (s==b) return k;
d=gcd(a,p);
}
ll x,y;
ex_gcd(s,p,x,y);
b=(b*x%p+p)%p;
ll tmp=bsgs(a,b,p);
if (tmp==-1) return -1;
else return tmp+k;
}
参考:
https://www.cnblogs.com/sdzwyq/p/9900650.html
《算法竞赛进阶指南》 李煜东 著
于HG机房