同余
// 常用知识
a==b(mod m) <---> m|(a-b)
a==b(mod m) 并且 a==b(mod n) --> m|(a-b) n|(a-b) ---> [n, m]|(a-b) --> a==b(mod [n, m])
(k, m) = d, ka = ka'(mod m) --> a==a'(mod m/d)
告诉了我们如何在同余式两边同时除掉一个东西,同时乘上一个数是显然正确的
证明:m|k(a-a') (m/d)|(k/d)(a-a') 所以有(m/d)和(k/d)互质,所以(m/d)|(a-a') 所以a=a'(mod m/d)
// 线性同余方程
ax==b(mod m) --> ax-my==b --> 扩展欧几里得求
扩展方法(感觉用处不是很大): ax=b(mod m), mx==0(mod m),对前面的x进行辗转相除就可以了,也能求到一个x的
解,主要的问题是后面b的位置的数可能需要开到longlong尽管a,b没有到longlong,而且需要较多的取mod操作,效
率也略低于扩展欧几里得
欧拉函数
// 简化剩余系
所有的n满足0<n<=m,(n, m)=1,构成了一个mod m的简化剩余系,其实就是0-m中和m互质的数和m构成的一个简化剩余系,记这样的个数是f(x)=m*求积(1-1/p), 其中p是m的质因数,公式的证明参考容斥
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
int main(){
int T;
cin >> T;
while(T--){
int n;
scanf("%d", &n);
int res = n;
for(int i = 2; i <= n/i; i++){
if(n % i == 0){
res = (LL)res * (i - 1) / i;
while(n % i == 0) n /= i;
}
}
if(n > 1) res = (LL) res * (n - 1) / n;
printf("%d\n", res);
}
return 0;
}
// 欧拉定理
(a, m) == 1, a^f(m) == 1(mod m), 其中f(m)为m的欧拉函数
特殊的有m是一个指数,f(m)=p-1, 所以有a^(p-1)==1(mod m)
假设与m互质的数是p1,p2....pf(m),有这样一个性质ap1,ap2....apf(m),只是上面一个另一种排列,那么
a^(f(m))p1p2..p(f(m))==p1p2..pf(m)(mod m)
所以有a^(f(m))=1(mod m)
求逆元
(a, m) = 1, ax=1(mod m), x是a的逆元
如果m是素数的话,a^(p-2)=1(mod m) 直接用费马小定理就可以了
如果m不是素数的话,a^(f(m) -1) = 1(mod m)
求1-n的逆元,使用递推减少一个log的常数
inv[i] = (p-p/i) * inv[p mod i] mod p
证明:p == 0 ==(p mod i) + (p/i)*i 一其中p/i是整除
-(p/i)*i = (p mod i) (mod p)
-p/i == (p mod i) * i^-1 ( mod p)
-p/i * (p mod i) ^-1 = i ^ -1 ( mod p)
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e7+10;
int inv[N];
int main(){
int p, n;
scanf("%d %d", &p, &n);
inv[1] = 1;
int ans = 1;
for(int i = 2; i <= n; i++){
inv[i] = (LL)(p - p/i) * inv[p % i] % p;
ans ^= inv[i];
}
printf("%d\n", ans);
return 0;
}
si=a1...ai
ti=si^-1=a1^-1....ai^-1
利用线性递推求出si和ti,然后ai^-1=si-1*ti
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e7+10;
int a[N], s[N], t[N];
unsigned int A, B, C;
int p, n;
inline unsigned int rng61() {
A ^= A << 16;
A ^= A >> 5;
A ^= A << 1;
unsigned int t = A;
A = B;
B = C;
C ^= t ^ A;
return C;
}
int exgcd(int a, int b, int &x, int &y){
if(!b){
x = 1, y = 0;
return a;
}
int d = exgcd(b, a%b, y, x);
y -= a/b*x;
return d;
}
int main() {
scanf("%d%d%u%u%u", &p, &n, &A, &B, &C);
int ans = 0;
for (int i = 1; i <= n; i++){
a[i] = rng61()%p;
if(a[i] == 0){
a[i] = 1;
ans ^= 1;
}
}
s[0] = 1;
for(int i = 1; i <= n; i++) s[i] = (LL)s[i-1] * a[i] % p;
// for(int i = 1; i <= n; i++) cout << s[i] << endl;
int x, y;
exgcd(s[n], p, x, y);
if(x < 0) x += p;
t[n] = x;
for(int i = n-1; i >= 1; i--) t[i] = (LL)t[i+1] * a[i+1] % p;
// for(int i = 1; i <= n; i++) cout << t[i] << endl;
for(int i = 1; i <= n; i++){
ans ^= (LL)s[i-1] * t[i] % p;
}
printf("%d\n", ans);
}