- 素数 因子只有1和本身;
#define maxn 100000
int vis[maxn], prime[maxn];//vis为1-合数,vis为0-素数1
int n;
void seize(int n) { //筛掉素数
int m = sqrt(n) + 0.5;
memset(vis, 0, sizeof(vis));
for (int i = 2; i <= m; i++) //因子最大不超过sqrt(n),以其为界
if (!vis[i]) {
for (int j = i * i; j <= n; j+=i) //最小质因子的倍数一定是合数;
vis[j] = 1;
}
}
void get_prime(int n) //打表
{
seize(n);
int c = 0;
for (int i = 2; i < n; i++) {
if (!vis[i]) prime[c++] = i;
}
}
- 欧几里得算法,一种求最大公约数的强大算法,
int gcd(int a, int b) {
return b == 0 ? a : gcd(b, a%b);
}
拓展欧几里得算法-不仅可以求出两个数a与b之间的余数,还可以求出关于
ax+by=gcd(a,b)的x,y的解:
两种方法
int exgcd(int a, int b, int &x, int &y) {
if (!b) {
x = 1, y = 0;
return a;
}
int r=exgcd(a, b, x, y);
int temp = x;
y = x - a/b*y;
x = temp;
return r;
}
蓝书的算法
void exgcd(int a, int b, int &r, int x, int y) {
if (!r) {
x = 1, y = 0;
r = a;
}
else {
exgcd(b, a%b, r, y, x);
y = y - x * (a / b);
}
}
问:拓展欧几里得算法为什么可以求出形如ax+by=gcd(a,b)的x,y的特解?(第一种方法)
答: 我们姑且将gcd(a,b)用g(a,b)表示,对于a和b而言,g(a,b)是它们的最大公约数.我们想一下拓展欧几里得递归的最后一层 a=g(a,b),b=0;
此时对于这一层的二元一次方程式带入后应该是,(g(a,b)x)+(by)=g(a,b)化简得 x=1,y=0;那么本层得x,y与上面一层之间有什么联系呢。换言之,我们现在讨论倒数第二层,本层x,y与倒数第一层x,y有什么联系呢。
我们先列出方程
本层 :ax0+by0=g(a,b);
下一层: bx1+(a%b)y1=g(a,b)
我们将两个方程联立 得出 ax0+by0=bx1+(a%b)y1
由于 a%b=a-(a/b)*b,我们将其替换,得;
ax0+by0=bx1+(a-(a/b)*b)y1
ax0+by0=bx1+ay1-(a/b)*by1,继续化简,得;
ax0+by0=ay1+b(x1-(a/b)y1),可得;
x0=y1,y0=x1-(a/b)y1,这样每一层的x,y 值都可以由上一层的x,y值得出来,于是乎,递推至最高,便可得出最高层的x,y值,注意,这只是一组特解;
通解可以用 y=y0-at,x=x0+at;(t为任意整数,),算我多说一句,这两个式子是如何得来的呢; ax+by=a(x0+at)+b(y0-at)=g(a,b);因此x=x0+at,y=y0-at(谁加,谁减都无所谓,因为t有正有负)记住,我们求解的是整数.
3.模运算
基本的加减:
(a + b)%c = (a%c + b%c)%c
(a − b)%c = (a%c − b%c + c)%c
- 乘积取模:
(ab)%c = (a%c)(b%c)%c
幂取模
a^bmod c=(a%c)*(a%c)^^^^
ll mul_mod(ll a, ll b, int n) {
return a * b%n;
}
ll pow_mod(ll a, ll p, ll n) {
if (!p) return 1;
ll ans = pow_mod(a, p / 2, n);
ans = ans * ans*a%n;
if (!(p & 1)) ans = ans * ans%n;
return ans;
}
4.欧拉函数。欧拉函数phi(x)等于不超过x且和x互素的整数个数。
p
h
i
(
m
)
=
∏
i
=
1
n
(
1
−
1
/
p
i
)
phi(m)=\prod^{n}_{i=1}(1-1/p_i)
phi(m)=i=1∏n(1−1/pi)
其中pi表示整数n的因子,每个因子只参与一次。
下面给出求单个值得不超过n且与n互素得正整数的个数
int euler_phi(int n) {
int m = (int)sqrt(n + 0.5);
int ans = n;
for (int i = 2; i <= m; i++) if (n%i == 0) {
ans = ans / i * (i - 1);
while (n%i == 0) n /= i; //将已经记录的因子除干净
}
if (n > 1) ans = ans / n * (n - 1);
}
打表的
int phi[10000];
void phi_table(int n) {
for (int i = 2; i <= n; i++) phi[i] = 0;
phi[1] = 1;
for (int i = 2; i <= n; i++) if (!phi[i]) { //从因子开始枚举
for (int j = i; j <= n; j += i) {
if (!phi[j]) phi[j] = j;
phi[j] = phi[j] * (i - 1) / i;
}
}
}