Loading

数论

老久以前的了,以前忘放上面了(差点丢了/jk)

欧拉筛素数

\(O(n)\)筛法

for(int i = 2; i <= n; i++) {
	  if(vis[i] == 0) pre[tot++] = i;//存下每个素数
	for(int j = 0; j <= tot, i * pre[j] <= n; j++){
		  vis[pre[j] * i] = 1;
		  if (i % pre[j] == 0){//保证每个数只筛一次
		  	  break;
		  } 
	}
}

保证每个数被它的最小质因子筛掉

为什么 (i % pre[j] == 0) 就 break ??

证明

  • \(pre\) 数组中的素数是递增的

  • \(i\) 能整除 \(pre_j\),那么 \(i * pre_{j+1}\) 这个合数肯定被 \(pre_j\) 乘以某个数筛掉

  • 因为 \(i\) 中含有 \(pre_j\) , \(pre_j\)\(pre_{j+1}\) 小,即 \(i = k * pre_j\)

  • 那么 \(i * pre_{j+1} = (k * pre_j) * pre_{j + 1}\)

  • \(pre_{j + 1} * k = k'\) 所以 \(i * pre_{j +1} = k'*pre_j\)

同余

在模意义下,加减乘都不会影响答案

加减很显然

证明乘??

  • 目标:\((x*y) ~mod~ p~= (x ~mod~ p) * (y ~mod~ p)\)

  • \(x = a_1*p + b_1,y = a_2 * p + b_2\)

  • \(x*y = (a_1*a_2 * p + a_1 * b_2 + b_1*a_2)*p + b_1*b_2\)

  • 很显然 \(x*y\)\(p\) 取模就是 \((b_1*b_2)\)\(p\) 取模

栗题

P4942 小凯的数字

我们把最后组合起来的数拆成每一位相加的形式,因为是在模9的意义下,所以 \(10^n~mod~ 9 = 1\) 所以最后答案也就是把每一位相加都取模

辗转相除法证明

  • \(gcd(a, b) = gcd(b, a~mod~b)\)

  • \(g|a\)\(g|b\), \(r = a - k * b = a~mod~b\)

  • 可得 \(\frac{r}{g}= \frac{a}{g} - \frac{k*b}{g}\)

  • 看得出,右边显然是个整数,所以 \(g | r\)

exgcd

可解方程

\[ax + by = gcd(a, b) \]

\(x,y\)

  • \(a' = b, b' = a~mod~ b\)

  • \(a'x + b'y = gcd(a', b')\)

  • 根据 \(gcd\)\(gcd(a', b') = gcd(a, b)\)

  • \(a'x + b'y = gcd(a, b)\)

  • \(bx + (a - b * \lfloor\frac{a}{b} \rfloor)*y = gcd(a, b)\)

  • \(ay + b(x - \lfloor \frac{a}{b}\rfloor*y) = gcd(a,b)\)

    \(\therefore x = y, y = x - \lfloor \frac{a}{b}\rfloor*y\)

一个回溯的过程

应用(扩欧解不定方程)

解形如 \(ax+by=c\) 的不定方程

先根据 \(exgcd\) 求出 \(ax + by = gcd(a, b)\) 的一组解 \(x_0, y_0\)

根据裴蜀定理,当 \(c ~mod~ gcd(a, b) = 0\) 的时候,此方程有解

求解

  • \(d = gcd(a , b)\)

  • 方程两边同时乘以 \(\frac{c}{d}\) , 得到 \(a*\frac{c}{d}x_0 + b*\frac{c}{d}y_0 = c\)

  • 然后就有了一组解 \(x_1 = \frac{c}{d}x_0, x_2 = \frac{c}{d}y_0\)

求最小整数解(使 \(x_1\) 最小化)

现在已经由 \(exgcd\) 求得任意一组 \(x, y\)

为了满足等式成立

那么 \(x\) 最小正整数解为 \((x + b) \% b\)

为什么??

可以这样想:

先在已经求出一组解使得 \(ax + by = 1\) 也就是 \(y = \frac{1 - ax}{b}\)

因为 \(y\) 是整数,所以此时 \(b | 1 - ax\) 现在想让 \(x\) 改变,如果 \(x\) 改变的值不是 \(b\) 的倍数,那就会使得 \(1 - ax\) 不能整除 \(b\)\(y\) 就不是整数了,所以 \(x\) 的改变量一定是 \(b\) 的倍数才可以

code

d = exgcd(a, b, x, y);//返回的是 a,b 的 gcd
if(c % d == 0)
{ 
    x *= c / d; 
    t = b / d;
    t = abs(t);
    x = (x % t + t) % t;
    printf("%d\n", x); 
}

栗题

P1516 青蛙的约会

/*
work by:Ariel_
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#define int long long
using namespace std;

int read(){
    int x = 0,f = 1; char c = getchar();
	while(c < '0'||c > '9') {if(c == '-') f = -1; c = getchar();}
	while(c >= '0' && c <= '9') {x = x*10 + c - '0'; c = getchar();}
	return x*f;
}
int x_1, y_1;
int exgcd(int a, int b, int &x1, int &y1){
   if(!b){
      x_1 = 1, y_1 = 0;
   	  return a;
   }
   int d = exgcd(b, a % b, x_1, y_1);
   int t = x_1; x_1 = y_1, y_1 = t - a / b * y_1;
   return d;
}
int x, y, m, n, l;
signed main(void){
    x = read(), y = read(), m = read(), n = read(), l = read();
    int a = n - m, b = l, c = x - y;
    int d = exgcd(a, b, x_1, y_1);
    if(c % d == 0) {
      x_1 *= c / d;
      int t = b / d;
      t = abs(t);
      x_1 = (x_1 % t + t) % t;
      printf("%lld", x_1);
	}
	else printf("Impossible");
	return 0;
}

中国剩余定理

\(m_1, m_2, m_3……m_n\) 是两两互质的正整数,有同余方程组

\[\begin{cases} x \equiv a_1 (mod~m_1)\\ x \equiv a_2 (mod~m_2)\\ ……\\ x \equiv a_n (mod~m_n)\\ \end{cases} \]

\(x\) ?

\(M = \prod_{i=1}^{n} m_i\)\(M_i = M/m_i\)\(t_i\) 是线性同余方程 \(M_it_i \equiv 1(mod ~m_i)\) 的一个解

\(x = \sum^{n}_{i = 1}a_iM_it_i\)

证明:

根据一个定理

两数不能整除,若除数扩大(或缩小)了几倍,而被除数不变,则其商和余数也同时扩大(或缩小)相同的倍数(余数必小于除数)

(证明: 假设 a % b = d,a / b = k, a = kb + d, a - kd = d,a,b 同时扩大 z 倍,得到 z(a - kd) = zd;)

所以可以得出 \(a_iM_it_i \equiv a_i(mod~~m_i)\)

显然 $ M_i$ 除了可以 \(m_i\) 之外都可以整除其他 \(m\)

\(\because \forall k \not= i,a_iM_it_i = 0(mod~m_k)\)

\(\because a_iM_it_i\equiv a_i(~mod~m_i)\)

所以带入原式得到:\(x = \sum^{n}_{i = 1}a_iM_it_i\)

可以结合 despair_ghost 这篇博客理解,讲的特别好

实现

回到上面的式子

  • \(M_it_i \equiv 1(mod ~m_i)\)

  • 可以得到 \(M_it_i - k*m = 1\)

  • \(M_i, m\) 都是定值,因为 \(M_i, m\) 互质,所以就可以 \(exgcd\) 求解了

【模板】中国剩余定理(CRT)/曹冲养猪

/*
work by:Ariel_
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#define int long long
using namespace std;
const int N = 20;
int read(){
    int x = 0,f = 1; char c = getchar();
	while(c < '0'||c > '9') {if(c == '-') f = -1; c = getchar();}
	while(c >= '0' && c <= '9') {x = x*10 + c - '0'; c = getchar();}
	return x*f;
}
int Ans, M = 1;
int n, m[N], a[N];
void exgcd(int a, int b, int &d, int &x, int &y){
    if (!b){d = a,x = 1, y = 0;return;}
      exgcd(b, a % b, d, x, y);
      int t = x; x = y, y = t - (a / b) * y;
} 
void Int_China() {
   int Mi, x, y, d; 
   for (int i = 1; i <= n; i++) {	    
	    Mi = M / m[i];
   	    exgcd(Mi, m[i], d, x, y);
   	    Ans = ((Ans + x * Mi * a[i]) % M + M) % M; 
   }
}
signed main(void) {
    n = read();
    for(int i = 1; i <= n; i++) m[i] = read(), a[i] = read(), M *= m[i];
	Int_China();
    printf("%lld\n",(Ans + M) % M );
	return 0;
}

扩展CRT

\(x\) ?

\[\begin{cases} x \equiv a_1 (mod~m_1)\\ x \equiv a_2 (mod~m_2)\\ ……\\ x \equiv a_n (mod~m_n)\\ \end{cases} \]

和中国剩余定理类似,就是没有互质的条件了

数学归纳法:

假设已经求出前 \(k-1\) 个方程组成的同余方程组的一个解为 \(x\)

且有 \(M=\prod_{i=1}^{i\leq k-1}\)

则前 \(k-1\) 个方程的方程组通解为 \(x+i*M(i\in Z)\)

那么对于加入第 \(k\) 个方程后的方程组

我们就是要求一个正整数 \(t\),使得 \(x+t*M \equiv a_k(mod~m_k)\)

转化一下上述式子得 \(t*M \equiv a_k-x(\mod m_k)\)

对于这个式子我们已经可以通过扩展欧几里得求解t

若该同余式无解,则整个方程组无解, 若有,则前 \(k\) 个同余式组成的方程组的一个解解为\(x_k=x+t*M\)

乘法逆元

解决模意义下没法进行除法的问题

已知 \(a|b\)\(\frac{b}{a}mod~m\)

设法把除法转化到乘法上去

找一个 \(a^{-1}\) 满足 $a*a^{-1}\equiv1(mod~m) $

换个形式

\(ax \equiv 1(mod~m)\)

根据扩展中国剩余定理就可以求出来了

可与忽视 p 不为质数的情况:只要 gcd(a,m) 不为 1,那么 a 的逆元就不存在

欧拉定理求逆元

\(inv[a] = a^{\phi(p) - 1}\) (保证 \(a\)\(p\) 互质)

欧拉函数

欧拉函数 \(\varphi(m)\) 表示不超过 \(m\) 且和 \(m\) 互素的正整数的个数

\(m = \prod p_i^{a_i}\)

求单值欧拉函数?

公式:

\[\varphi(n) = n*\prod^k_{i = 1}(1 - \frac{1}{p_i}) \]

\(p_1,p_2…p_k\)\(n\) 的所有质因子

证明

考虑容斥

因为要求与 \(n\) 互质的数的个数,因为在 \(n\) 之内,每一个 \(p_i\) 的倍数分布是均匀的, \(n\) 之内有 \(\frac{1}{p_i}\) 的数是 \(p_i\) 的倍数,因此有 \(n*(1 - \frac{1}{p_i})\) 个数不是 \(p_i\) 的倍数,同理有 \(n*(1 - \frac{1}{p_j})\) 个数不是 \(p_j\) 的倍数,根据乘法原理,有 \(n*(1 - \frac{1}{p_i})* (1 - \frac{1}{p_j})\) 个数既不是 \(p_i\) 的倍数,也不是 \(p_j\) 的倍数。进而就可以推广得到有 \(n*\prod^{k}_{i = 1}(1 - p_i)\) 个数与 \(n\) 互质 (有点生物算遗传的意思)

严谨证明(来自 \(attack\) 大佬)

1.当 \(n=1\)

很明显,答案为 \(1\)

2.当 \(n\) 为质数时

根据素数的定义,答案为 \(n−1\)

3.当 \(n\) 为合数时

我们已经知道了 \(n\) 为素数的情况

\(n\) 进行质因数分解

\(n=a^{p_{1}^1}∗a^{p_{2}^{2}}...∗a^{p_{k}^k}\)

假设 \(k=1\)

那么 \(\varphi(p^k)=p^k−p^{k−1}\)

证明:考虑容斥,与一个数互素的数的个数就是这个数减去与它不互素的数的个数

因为 \(p\) 是素数,所以在 \(p^k\) 中与其不互素的数为 \(1∗p,2∗p....p^{k−1}∗p\),有\(p^{k−1}\)

\(k\neq 1\)

  • \(\varphi(n) = \varphi(a_1^{p_{1}^1}∗a_2^{p_{2}^{2}}...∗a_k^{p_{k}^k})\)

  • \(\prod^k_{i = 1}a_i^{p_i} - a_{i}^{p_{i - 1}}\)

  • \(\prod^k_{i = 1}a_i^{p_i}(1-\frac{1}{p_i})\)

  • \(n*\prod^k_{i = 1}(1-\frac{1}{p_i})\)

单值求法:若 \(gcd(a, b) = 1\) 就更新 \(phi_n\) 的值

\(1-n\)\(\varphi(i)\)

埃氏筛

\(O(n)\) 扫一遍,每扫到一个质数,就更新一下它的所有的倍数

时间复杂度:\(O(n*ln ln n)\)

/*
work by:Ariel_
*/
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N = 1e5 + 4;
int read(){
    int x = 0,f = 1; char c = getchar();
	while(c < '0'||c > '9') {if(c == '-') f = -1; c = getchar();}
	while(c >= '0' && c <= '9') {x = x*10 + c - '0'; c = getchar();}
	return x*f;
}
int n, phi[N];
int main(void){
    n = read();
    for (int i = 1; i <= n; i++) phi[i] = i;
    for (int i = 2; i <= n; i++){
    	if(phi[i] == i) {
    	   for (int j = i; j <= n; j += i)
    		  phi[j] = phi[j] / i * (i - 1);
		}
	}
	for(int i = 1; i <= n; i++){
		printf("%d\n", phi[i]);
	}
	return 0;
}

康托展开

康托展开是全排列到自然数的一一映射,该自然数可以代表该序列在全排列中的排名(从0开始)

正康托展开:全排列到名次

\(a_5 = \lbrace2,3,5,1,4\rbrace\)

求该序列的名次

考虑比它小的序列

\(1*4!+1*3!+2*2!+0+0 = 34\) 排列组合

可以这么理解:后面比当前小的数的个数*后面数的全排列

逆康托展开:名次到全排列

\(1*4!+1*3!+2*2!+0+0 = 34\)

得到 \(a_5 = \lbrace2,3,5,1,4\rbrace\)

卢卡斯定理/Lucas 定理

这直接给出公式,证明过程涉及二项式内容,感性理解 = =

\(C_n^m = Lucas(n, m, p)\)

\(Lucas(n, m, p) = C(n\%p,m\%p) * Lucas(\frac{n}{p }, \frac{m}{p}, p)\)

\(Lucas(x, 0, p) = 1, C(a, b) = (\frac{a!}{b!\times(a - b)!}^{p - 2})~mod~p\)

int n, m, p, a[N];//a:阶乘 
int qpow(int x, int y) {
   int tmp = 1;
   while(y) {
   	 if (y & 1) tmp = (tmp * x) % p;
   	 y >>= 1;
   	 x = (x * x) % p;
   }
   return tmp;
}
int C(int n, int m) {
   if (m > n) return 0;
   return (a[n] * qpow(a[m], p - 2) % p * qpow(a[n - m], p - 2) % p) % p;
}
int Lucas(int n, int m) {
	if (!m) return 1;
    return C(n % p, m % p) * Lucas(n / p, m / p) % p;
}
signed main(){
   int T = read();
   while(T--) {
   	 a[0] = 1;
   	 n = read(), m = read(), p = read();  
     for (int i = 1; i <= p; i++) a[i] = a[i - 1] * i % p;
	 printf("%lld\n", Lucas(n + m, n)); 
   }
   return 0;
}

整数分块

\(\sum_{i = 1}^{n}(\lfloor \frac{n}{i}\rfloor)^5 \times i\)​​

\(n \leq 10^9\) 答案对 \(10^9 + 7\) 取模。

引理一:$\lfloor \frac {n}{i} \rfloor $​ 最多只有 \(2\sqrt{n}\)​ 种不同的取值。

证明:将 \(i\) 分为大于等于 \(\sqrt{x}\) 与大于 \(\sqrt{x}\) 的两部分

  • \(d \leq \sqrt{x}\)

此时 \(\lfloor{\frac{x}{d}}\rfloor \geq \sqrt{x}\),最多 \(\sqrt{x}\)​ 种取值

  • \(d > \sqrt{x}\)

\(\lfloor \frac{x}{d} \rfloor < \sqrt{x}\), 最多 \(\sqrt{x}\) 种取值

复杂度为 \(O(\sqrt{x})\)

引理二:所有$\lfloor \frac {n}{i} \rfloor $ 相同的对应的 i 一定是一段连续的区间

反证法:如果不是连续的区间

一定存在 \(l < t < r\)​ 使得 \(\lfloor \frac{n}{l}\rfloor = \lfloor \frac{n}{r}\rfloor\)​ 且\(\lfloor \frac{n}{l}\rfloor \neq \lfloor \frac{n}{t}\rfloor\)

\(\because t > l\)

\(\because \lfloor \frac{n}{t}\rfloor < \lfloor \frac{n}{l}\rfloor\)

\(\because t < r\)

\(\because \lfloor \frac{n}{t}\rfloor > \lfloor \frac{n}{r}\rfloor\)

所以假设不成立

引理三:对于所有连续的区间,如果知道了 l ,那么 r 也会知道

$r = \lfloor{\frac{n}{\lfloor\frac{n}{l}\rfloor}}\rfloor $

证明?我不会

//f(a) = a^5;
for (int i = 1, last; i <= n; i = last + 1) {
      int a = n / i;
      last = n / a;
      ret += f(a) * (sum[last] - sum[i - 1]);
}
posted @ 2021-06-25 21:46  Dita  阅读(146)  评论(2)    收藏  举报