void exgcd(ll a, ll b, ll &r, ll x, ll y) {
if (!r) {
x = 1, y = 0;
r = a;
}
else {
exgcd(b, a%b, r, y, x);
y = y - x * (a / b);
}
}
ll china(int n, int *a, int *m) {
ll M = 1, v, y,d, x = 0;
for (int i = 0; i < n; i++) M *= m[i];//首先我们将 所有的m相乘得出
for (int i = 0; i < n; i++) {
ll w = M / m[i];//对于每一个式子,我们找出除了这个式子本身以外的所有除数的乘积 令w=M/mi;
exgcd(m[i], w, d, d, y); //求逆元
x = (x + y * w*a[i]) % M;//我们求出w以m~i~的逆元然后乘上a~i~,对M进行取模,累加所有结果,
}
return (x + M) % M;//最后再次对M取模,得出最小正整数
}
- 对数模方程
求解类似 ax=b(mod n)的方程,n为素数。
根据欧拉定理,只检查x=0,1,2,……,n-1是不是解即可。因为an-1=1(mod n) (由费马小定理得出)当x超过n-1的时候 ax的解就开始了循环。我们先检查前m项(m的值一般取sqrt(n),m为整数),即a0,a1,a2……am-1的模是否为b,并把ajmod n的值存进e[j]里面,求出am的逆a–m;
下面考虑am,am+1,am+2……a2m-1,这次不用一一检查,通过e数组来进行判断。如果它们之中一定有解,则ai+m=e[i]am=b(mod n);我们把两边同乘上a-m,b’=b(a-m);
这样只需要检查ei下标中是否真的存在b’这个值就行了
代码如下
typedef long long ll;
void exgcd(ll a, ll b, ll &r, ll x, ll y) {
if (!r) {
x = 1, y = 0;
r = a;
}
else {
exgcd(b, a%b, r, y, x);
y = y - x * (a / b);
}
}
ll pow_mod(ll a, ll m, ll n) {
int ans = pow_mod(a, m / 2, n);
ans =(ll)ans * ans%n;
if (m & 1) ans = (ll)ans*a%n;
return ans;
}
ll mul_mod(ll a, ll b, ll n) {
return ((a%n)*(b%n)) % n;
}
ll inv(ll a, ll n) {
ll d, x, y;
exgcd(a, n, d, x, y);
if (d == 1) return (x + n)%n;
else return -1;
}
int log_mod(int a, int b, int n) { //核心代码
ll m, v, i,e=1;
m = sqrt(n+0.5);
map<int, int > x;
v = inv(pow_mod(a, m, n), n);
for (int i = 1; i < m; i++) { //计算e[i];
e = mul_mod(a, e, n);
if (!x.count(e)) x[e] = i;
}
for (int i = 0; i < m; i++) {
if (x.count(b)) return i * m + x[b];
b = mul_mod(b, v, n);
}
return -1;
}