@WangYixu
2018-06-14T13:09:50.000000Z
字数 8676
阅读 823
OI 数学 笔记 算法
主要研究整数的性质
定义3.1:,则n为质数,否则n为合数.
定理3.2:(素数定理)
定理3.3:对于等号右边的式子唯一.(算数基本定理)
也可写作.
0.枚举所有到的整数,检查是否为因数,复杂度.
1.枚举到的所有正整数,检查是否为因数,.
2.枚举到的所有素数,检查是否为因数,.
3.Miller-Rabin.
枚举到的所有质数,不断试除至除净.
最后可能会剩一个大的素因子.
notp[1] = 1;for (int i = 2; i <= n; i ++)if (!notp[i])for (int j = i * i; j <= n; j += i)notp[j] = 1;
约定每个数只被它的最小质因子筛去。
notp[1] = 1;for (int i = 2; i <= n; i ++) {if (!notp[i]) pri[++ psz] = i;for (int j = 1; j <= psz; j ++) {int k = i * pri[j];if (k > n) break ;notp[i * pri[j]] = 1;if (i % pri[j] == 0) break;//pri[j]是i的素因子,那么pri[j+1]*i的最小素因子不是pri[j+1],按照约定,跳出循环。}}
给定,求范围内的素数。
\\预先筛好[1,sqrt(r)]范围内的素数for (int i = 1; i <= psz; i ++) {int p = pri[i];for (int j = (l - 1) / p + 1/*防止下标小于0*/; j <= r / p; j ++)notp[j * p - l] = 1;}
定理5.1:.(除法定理)
定义5.2:称5.1中的 为除法的商,为余数.
.
int gcd(int a, int b) {return !b ? a : gcd(b, a % b);}
定理6.2:有整数解(裴蜀定理)
引理6.2.1:的最小正值为.
推论6.2.2:.
推论6.2.3:.
推论6.2.4:.
证明:
推广:
- 如果,不合法,输出零。
- 对和,分别唯一分解,对于某个质因子,其指数为,如果,那么的的指数只能为,如果,那么中必有一个数的指数为,一个为,一个满足,这样的三元组一共有种;
这里做了一个小改动:直接分解
本来还打了素数表,不知道为什么WA了
inline void factor() {memset(g, 0, sizeof(g));memset(l, 0, sizeof(l));if (L % G) {printf("0\n");return;}ll ans = 1;L /= G;for (register int i = 2; L > 1; ++i) {int cnt = 0;while (L % i == 0 && L > 1) {L /= i;cnt++;}if (cnt) ans *= 6 * cnt;}printf("%d\n", ans);}
求解形如的不定方程。
void exgcd(int a, int b, int& d, int &x, int &y) {if (!b) x = 1, y = 0, d = a;else {d = exgcd(b, a % b, d, y, x);y -= (a / b) * x;}}
该算法解出了的一组解,其通解表示为。
模板题
设相遇时间为,那么,两只青蛙相遇可以表示为,即,化简得。
扩欧解即可。
转化成
枚举即可。
注意要从最大洞穴编号开始枚举。
就这个我WA了3次。
扩欧解即可。
也可以用费马小定理求。
可以在的时间内处理的逆元:
考虑以下式子:
设
即
这样就可以O(1)递推了
inv[1] = 1;for(int i = 2; i <= n; ++i) {inv[i] = ((-inv[P % i] * (P / i) ) %P+P)% P;}
费马小定理没有逆定理
虽然费马小定理没有逆定理,但是费马小定理的逆否命题是正确的,所以我们可以用费马小定理来验证一个数是否是合数,这样,多次选取a的值,就能大致确定一个数是否是质数。
可惜总有一些数是特殊的,能卡掉这种算法。
所以我们就要使用Miller-Rabin算法
对于一个可能是质数的数,如果它是质数,那么,其必然满足:
而由于二次探测定理,就有
如果余,则可以递归下去检验,如果余,那么停止递归,我们就认为是一个可能的素数。
多次测试,避免偶然性。
实践中多自下而上操作。
考虑构造一个数,满足且最小。
那么,由二次探测定理,必然有或.
bool Miller-Rabin(int p, int T) {if (p == 2) return true;if (p < 2) return false;int t = p-1, x = 0;while (!(t&1)) {t >>= 1;x++;}while (T--) {int a = rand() % (p-1) + 1;if(pow_mod(a, t, p) == 1) continue;flag = 0;for (int i = 1; i <= x; ++i) {if (pow_mod(a, (t<<i), p) == p-1) {flag = 1;break;}}if (!flag) return false;}return true;}
对于模线性方程组:
当时有唯一解,
方法一:枚举,naive!
方法二:整除分块
int ans = 0, n = 100;for (int i = 1, j; i <= n; i = j + 1) {j = min(n, (n/(n/i)));//右端点,玄学操作ans += (n/i)*(j-i+1);}printf("%d\n", ans);
转化成,
整除分块,注意到商相同时,后面的求和部分成等差数列,做即可。
ll ans = n * k;for (ll i = 1, j; i <= n; i = j + 1) {if(k / i) j = min(n, (k/(k/i)));else j = n;ans -= (j-i+1)*(i+j)*(k/i)/2;}printf("%lld\n", ans);
题解
定义域为整数的函数
基于以下三条定理,我们可以线性的求出欧拉函数表
定理1易证,定理3是欧拉函数的性质,下面给出定理2的证明:
notp[1] = phi[1] = 1;for (int i = 2; i <= n; i ++) {if (!notp[i]) pri[++ psz] = i, phi[i] = i - 1;for (int j = 1; j <= psz; j ++) {int k = i * pri[j];if (k > n) break ;notp[k] = 1;if (i % pri[j] == 0) {phi[k] = phi[i] * pri[j]; //markbreak;} else {phi[k] = phi[i] * phi[pri[j]];}}}
可知当有质因子时平方因子时.
莫比乌斯函数可以线性筛
notp[1] = phi[1] = mu[1] = 1;for (int i = 2; i <= n; i ++) {if (!notp[i]) pri[++ psz] = i, phi[i] = i - 1, mu[i] = -1;for (int j = 1; j <= psz; j ++) {int k = i * pri[j];if (k > n) break ;notp[k] = 1;if (i % pri[j] == 0) {mu[k] = 0;//有平方因子break ;} else {mu[k] = -mu[i];//无平方因子,多了一个素因子}}}
有如下两条定理:
定理10.1:
定理10.2:
解:
解:
以上两个反演十分基础,必须全面理解。
解:
定义1.1排列:的数组成的排列(不重)。
定义1.2逆序对:.
定义1.3对换:交换一个排列中的两个数。
定义1.4奇\偶排列:按逆序对的数目。
定理1.5:对换改变排列的奇偶性。
定理1.6:在全部阶排列中,奇偶排列各占一半
定理1.7:任意一个排列可经过一系列对换变成自然排列,并且所做对换次数的奇偶性与这个排列的奇偶性相同。
a[n][n],y[n];do {res = 1;for (int i = 1; i <= n; ++i)res *= a[i][y[i]];if(isOdd(y)) res = -res;ans += res;} while (next_permuation(y+1, y+n+1))
引理2.1:行列互换,值不变。
引理2.2:用一个数乘行列式的某行等价于行列式的值乘这个数。
引理2.3:可以把一行的值拆成两部分,从而拆开行列式,从而形成两个行列式,其和等于原行列式。
引理2.4:对换行列式中两行的位置,行列式反号。
引理2.5:如果行列式中有两行成比例,则行列式的值为0.
引理2.6:把一行的某个倍数加到另一行,行列式的值不变。
定义:行列式去掉某一行和某一列。()
代数余子式:
线性方程组的系数行列式时有唯一解。
其中为将中的第列换成等号右边的数的行列式。
个点的完全图生成树的个数为