签到题爆零系列。。。
开局先说些有的没的。这道题是9月月赛的T1,题目名字叫做“签到题”,结果难度给了省选。。。真的毒瘤!
题面就是让你求\(11\cdots1111(N个1) \equiv K \pmod m\)。其中他说\(m\)是质数。
暴力模拟一分都拿不到!很气!
下面我们来看正解。。。
其实这\(N个1\)是可以有通项公式的。数学必修五学到这个公式。\(a_n=\frac{10^n-1}{9}\)。
所以那个同余的式子就能变成\(\frac{10^n-1}{9} \equiv K \pmod m\)。
同余的等号也有类似于一般的等号。我们可以左右各乘以9再加1。结果就变成了\(10^n \equiv 9K+1 \pmod m\)。这种求指数的同余方程叫做什么离散对数。
接下来介绍一个名字叫做大步小步算法(baby-step giant-step algorithm)。这个算法就是专门来解决这类问题的。
我们考虑一个一般性的方程\(y^x \equiv z \pmod p\)。
温馨提示:下面的东西有点玄学。。。
我们设\(m=\lceil \sqrt p \rceil\),令我们要求的\(x=a \times m-b\)。这里很巧妙设为减,后面有大作用。
所以变成了\(y^{a \times m - b} \equiv z \pmod p\),移项得\(y^{a \times m} \equiv z \times y^b \pmod p\)。
这里注意下取值范围:上面我们定义了两个新的变量\(a和b\)。\(a \in (0,m+1],b \in [0,m)\)。
我们先算出所有的\(z \times y^b\)的值,然后用一个hash表记录下来,我们就能用值来取出指数\(b\)。这里应该是baby-step部分。
处理完右边,我们可以处理左边了。这里把\(y^{a\times m}\)变成\({y^m}^a\)。这里是giant-step部分:弄出一个变量giant
为\(y^m\)。然后我们套\(a\)次的循环,每一次循环算出左边的模值,看看hash表里面有没有,如果有的话你就找到解了,解的值为\(i \times m - b\)返回掉。
但是这个方程也可能是无解的。我们拿-1来作为无解的标志就行了。
差不多就是这样了。看起来算法思想很玄学,但是实现起来还可以。
对了,这个算法有前置技能:快速乘和快速幂。一个使用乘法分配律,用加的,另一个用指数的性质,用乘的。
注意:快速幂的乘可以用快速乘来优化。所谓的快速乘就是来防止你乘完中间过程还是什么的爆long long。
快速乘有\(O(logn)\)的,也有\(O(1)\)的。这里卡log的快速乘。。。真的毒瘤!
代码:
#include<cstdio>
#include<cmath>
#include<map>
#define ll long long
ll k, mod;
ll mul_mod(ll a, ll b)
{
ll ans = 0, base = a;
while(b)
{
if(b & 1) ans = (ans + base) % mod;
base = (base + base) % mod;
b >>= 1;
}
return ans % mod;
}
ll mult(ll a,ll b){
auto ans=a*b-(ll)((long double)a/mod*b+1e-10)*mod;
return (ans+mod)%mod;
}
ll pow_mod(ll a, ll b)
{
ll ans = 1, base = a;
while(b)
{
if(b & 1) ans = mult(ans, base);
base = mult(base, base);
b >>= 1;
}
return ans % mod;
}
ll bsgs(ll a, ll b)
{
// a^x === b (mod mod)
// x = a * m - b
// a^(a * m - b) === b (mod mod)
// a^(a * m) === b * a^b (mod mod)
if(a == 0)
{
if(b == 0) return 1;
else return -1;
}
std::map<ll, ll> hash;
b = b % mod;
ll m = sqrt(mod) + 1;
for(int i = 0; i < m; i++)
{
ll val = mult(b, pow_mod(a, i));
if(!hash.count(val)) hash[val] = i;
}
ll giant = pow_mod(a, m);
for(int i = 0; i <= m; i++)
{
ll val = pow_mod(giant, i);
ll j = hash.find(val) == hash.end() ? -1 : hash[val];
if(j >= 0 && i * m - j >= 0) return i * m - j;
}
return -1;
}
int main()
{
scanf("%lld%lld", &k, &mod);
ll ans = bsgs(10, 9 * k + 1);
printf("%lld\n", ans);
return 0;
}