数学基础 3
前言
想了一下 前面那个还是太多了 所以还是再开一个比较好
写不动了 受限于个人水平 后面的写不下去了 所以跑去搞别的(雾
根本学不会
指数方程
形如: (a^x equiv b mod m) 的方程
求解: BSGS (半死龟速算法)
可以在 (O(sqrt m)) 的时间内求解 (a^x equiv b mod m) 要求 (a ot m) 不一定要求 (m) 为素数
设定一个常量 (T) 使 (x = qT - r) 其中 (0 leq r < T) 则原方程可以转化:
(a^{qT - r} equiv b mod m \ a^{qT} equiv a^rb mod m)
考虑预处理所有的 (a^rb mod m) 的值 用 (hash) 或者是 (map) 存起来 求解时枚举 (q) 计算 (a^{qT}) 判断哈希表或者 (map) 中是否出现了 (a^{qT}) 出现则说明等式成立 方程有解
预处理枚举至多 (T) 个 (r) 复杂度 (O(T)) 查询时至多 (frac nT) 个 (a^{qT} mod m) 复杂度 (O(frac mT)) 总复杂度 (O(frac mT + T)) 在 (T = sqrt m) 时达到平衡 复杂度 (O(sqrt m))
题目: BSGS
代码
/*
Time: 6.20
Worker: Blank_space
Source: P3846 【模板】BSGS
*/
/*--------------------------------------------*/
#include<cstdio>
#include<cmath>
#include<map>
#define int long long
/*--------------------------------------头文件*/
const int A = 1e4 + 7;
const int B = 1e5 + 7;
const int C = 1e6 + 7;
const int D = 1e7 + 7;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
/*------------------------------------常量定义*/
inline void File() {
freopen(".in", "r", stdin);
freopen(".out", "w", stdout);
}
/*----------------------------------------文件*/
int b, p, n, T;
std::map <int, int> mp;
/*------------------------------------变量定义*/
inline int read() {
int x = 0, f = 1; char ch = getchar();
while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
return x * f;
}
/*----------------------------------------快读*/
int power(int _a, int _b, int res = 1) {for(; _b; _a = _a * _a % p, _b >>= 1) if(_b & 1) res = res * _a % p; return res;}
void BS() {
T = ceil(sqrt(p)) + 1; int sum = n;
for(int r = 0; r < T; r++) mp[sum] = r, sum = sum * b % p;
}
bool GS() {
int bt = power(b, T), sum = bt;
for(int q = 1; q <= T; q++)
{
if(mp.count(sum)) {printf("%lld", q * T - mp[sum]); return 1;}
sum = sum * bt % p;
}
return 0;
}
/*----------------------------------------函数*/
signed main() {
p = read(); b = read(); n = read();
if(!(b % p)) {puts("no solution"); return 0;}
BS(); if(!GS()) puts("no solution");
return 0;
}
**扩展BSGS **
仍是求解 (a^x equiv b mod m) 但是不保证 (a ot m)
由于阶和原根没学的原因 OI-wike 上的没有看懂...
考虑将上面那个转换为一般的 BSGS
设 (d_1 = (a, m)) 若 (d_1 mid b) 则原方程无解 否则令方程两边同除 (d_1) 得:(frac a{d_1} imes a^{x - 1} equiv frac b{d_1} mod frac m{d_1}) 若 ((a, frac m{d_1}) e 1) 设 (d_2 = (a, frac m{d_1})) 重复上述步骤 知道 ((a, frac m{prod_{i = 1}^kd_k}) = 1) 为止
此时方程为: (frac {a^k}{prod_{i = 1}^kd_k} imes a^{x - k} equiv frac b{prod_{i = 1}^kd_k} mod frac {m}{prod_{i = 1}^kd_k})
然后按照 BSGS 来搞就好了 枚举 (a^{qT}) 的时候乘上常数 (frac {a^k}{prod_{i = 1}^kd_k}) 即可
注意枚举 (k) 的时候判断 (a^{x - k} equiv b mod m) 是否存在 注意保证指数不为负数
题目: 扩展BSGS
这个题的原题是卡 (map) 的 但是在这个数据比较弱的模板题上 (map) 是能过的
代码
/*
Time: 6.20
Worker: Blank_space
Source: P4195 【模板】扩展BSGS
*/
/*--------------------------------------------*/
#include<cstdio>
#include<cmath>
#include<map>
#define int long long
/*--------------------------------------头文件*/
const int A = 1e4 + 7;
const int B = 1e5 + 7;
const int C = 1e6 + 7;
const int D = 1e7 + 7;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
/*------------------------------------常量定义*/
inline void File() {
freopen(".in", "r", stdin);
freopen(".out", "w", stdout);
}
/*----------------------------------------文件*/
int a, b, p, T, d;
std::map <int, int> mp;
/*------------------------------------变量定义*/
inline int read() {
int x = 0, f = 1; char ch = getchar();
while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
return x * f;
}
/*----------------------------------------快读*/
int power(int _a, int _b, int res = 1) {for(; _b; _a = _a * _a % p, _b >>= 1) if(_b & 1) res = res * _a % p; return res;}
int gcd(int _a, int _b) {return _b ? gcd(_b, _a % _b) : _a;}
int BSGS(int ad) {
mp.clear();
T = ceil(sqrt(p)) + 1; int sum = b % p;
for(int r = 0; r < T; r++) mp[sum] = r, sum = sum * a % p;
int at = power(a, T); sum = ad;
for(int q = 0; q <= T; q++)
{
if(mp.count(sum) && q * T - mp[sum] >= 0ll) return q * T - mp[sum];
sum = sum * at % p;
}
return -1;
}
int exBSGS() {
a %= p; b %= p; if(b == 1 || p == 1) return 0;
int k = 0, d, ad = 1;
while((d = gcd(a, p)) != 1)
{
if(b % d) return -1;
k++; b /= d; p /= d; ad = ad * a / d % p;
if(ad == b) return k;
}
int ans = BSGS(ad);
return ~ans ? BSGS(ad) + k : -1;
}
/*----------------------------------------函数*/
signed main() {
while(1)
{
a = read(); p = read(); b = read();
if(!a && !p && !b) return 0;
int ans = exBSGS();
if(~ans) printf("%lld
", ans); else puts("No Solution");
}
return 0;
}
任务一直接快速幂即可
任务二扩展欧几里得
任务三 BSGS 即可
有一点需要注意
一般来说 在 (a^x equiv b mod p) 中 当 (b mid p) 的时候是无解的 但是如果 (a mid p, b mid p) 同时成立的话 方程是有解的 最小整数解为 (x = 1) 需要判断 否则最后一个点过不去
代码
/*
Time: 6.20
Worker: Blank_space
Source: P2485 [SDOI2011]计算器
*/
/*--------------------------------------------*/
#include<cstdio>
#include<cmath>
#include<map>
#define int long long
/*--------------------------------------头文件*/
int _T, T, k, x, y, p, d, a, b;
std::map <int, int> mp;
/*------------------------------------变量定义*/
inline int read() {
int x = 0, f = 1; char ch = getchar();
while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
return x * f;
}
/*----------------------------------------快读*/
int power(int _a, int _b, int res = 1) {for(; _b; _a = _a * _a % p, _b >>= 1) if(_b & 1) res = res * _a % p; return res;}
void exgcd(int _a, int _b, int &_d, int &_x, int &_y) {
if(_b) exgcd(_b, _a % _b, _d, _y, _x), _y -= _x * (_a / _b);
else _d = _a, _x = 1, _y = 0;
}
void work1() {printf("%lld
", power(a, b));}
void work2() {
a %= p; b %= p;
if(!b) {puts("0"); return ;}
exgcd(a, p, d, x, y); x = x * b / d;
if(b % d) {puts("Orz, I cannot find x!"); return ;}
printf("%lld
", (x % p + p) % p);
}
void BS() {
mp.clear(); T = ceil(sqrt(p)) + 1; int sum = b % p;
for(int r = 0; r < T; r++) mp[sum] = r, sum = sum * a % p;
}
bool GS() {
int at = power(a, T), sum = at;
for(int q = 1; q <= T; q++)
{
if(mp.count(sum)) {printf("%lld
", q * T - mp[sum]); return 1;}
sum = sum * at % p;
}
return 0;
}
void work3() {
if(b % p == 1) {puts("0"); return ;}
if(!(a % p) && !(b % p)) {puts("1"); return ;}
if(!(a % p)) {puts("Orz, I cannot find x!"); return ;}
BS(); if(!GS()) puts("Orz, I cannot find x!");
}
void work() {
a = read(); b = read(); p = read();
if(k == 1) work1();
if(k == 2) work2();
if(k == 3) work3();
}
/*----------------------------------------函数*/
signed main() {
_T = read(); k = read(); while(_T--) work();
return 0;
}
题意简述
给定 (p, a, b, x_1, t) 已知 (x_i equiv a imes x_{i - 1} + b mod p) 求 (i_{min}) 使 (x_i = t) 成立
写一下递推式
(x_1 = x_1 \ x_2 = ax_1 + b \ x_3 = a^2x_1 + ab + b \ x_4 = a^3x_1 + a^2b + ab + b \ ...)
不难得出 (x_i = a^{i - 1}x_1 + sum_{j = 0}^{i - 2}a^jb)
后面那一坨是等比数列的形式 再化一下 有 (x_i = a^{i - 1}x_1 + frac {b(a^{i - 1} - 1)}{a - 1})
继续搞
(x_i equiv a^{i - 1}x_1 + frac {b(a^{i - 1} - 1)}{a - 1} \ x_i(a - 1) equiv a^{i - 1}(a - 1)x_1 + b(a^{i - 1} - 1) \ ax_i - x_i equiv a^{i - 1}(ax_1 - x_1) + a^{i - 1}b - b \ ax_i - x_i + b equiv a^{i - 1}(ax_1 - x_1) + a^{a - 1}b \ ax_i - x_i + b equiv a^{i - 1}(ax_1 - x_1 + b) \ a^{i - 1} equiv frac {ax_i - x_i + b}{ax_1 - x_1 + b})
我们的目的是求 (i) 使 (t = x_i)
直接将 (t) 代入 有 (a^{i - 1} equiv frac {at - t + b}{ax_1 - x_1 + b})
可以发现这是一个长得比较 BSGS 的式子
跑一下 答案加一即可
细节比较多
首先是当输入的 (x_1) 直接等于 (t) 的时候 直接输出 (1)
当 (a = 1) 的时候 原来的递推式变成了一个等差数列 不难推出 (x_i = x_1 + (i - 1)b) 代入 (t) 移项 得 (i - 1 = frac {t - x_1}b) 判一下 (b) 是否为 (0) 输出即可
当 (a = 0) 的时候 上式为 (0 equiv frac {b - t}{b - t}) 当 (b = t) 时是有解的 但解是多少 回到原来的递推中 不难发现 当 (i > 1) 的时候 所有项都为常量 (b) 答案即为 (2)
代码
/*
Time: 6.20
Worker: Blank_space
Source: P3306 [SDOI2013] 随机数生成器
*/
/*--------------------------------------------*/
#include<cstdio>
#include<cmath>
#include<map>
#define int long long
/*--------------------------------------头文件*/
inline void File() {
freopen(".in", "r", stdin);
freopen(".out", "w", stdout);
}
/*----------------------------------------文件*/
int p, a, b, x, t, T, _T, k;
std::map <int, int> mp;
/*------------------------------------变量定义*/
inline int read() {
int x = 0, f = 1; char ch = getchar();
while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
return x * f;
}
/*----------------------------------------快读*/
int power(int _a, int _b, int res = 1) {for(; _b; _a = _a * _a % p, _b >>= 1) if(_b & 1) res = res * _a % p; return res;}
void BS() {
mp.clear(); T = ceil(sqrt(p)) + 1; int sum = k;
for(int r = 0; r < T; r++) mp[sum] = r, sum = sum * a % p;
}
bool GS() {
int at = power(a, T), sum = at;
for(int q = 1; q <= T; q++)
{
if(mp.count(sum)) {printf("%lld
", q * T - mp[sum] + 1); return 1;}
sum = sum * at % p;
}
return 0;
}
void _work() {
k = ((a * t - t + b) % p + p) % p * power(((a * x - x + b) % p + p) % p, p - 2) % p;
if(!(a % p) && !(k % p)) puts("2"); else if(!(k % p)) puts("-1");
else {BS(); if(!GS()) puts("-1");}
}
void work() {
p = read(); a = read(); b = read(); x = read(); t = read();
if(x == t) puts("1");
else if(a == 1) printf("%lld
", b ? (t - x % p + p) % p * power(b, p - 2) % p + 1 : -1ll);
else if(a == 0) if(b == t) puts("2"); else puts("-1");
else _work();
}
/*----------------------------------------函数*/
signed main() {
_T = read(); while(_T--) work();
return 0;
}
积性函数
定义 : 若 (gcd(x, y) = 1) 且 (f(xy) = f(x)f(y)) 则 (f(n)) 为积性函数
性质
若 (f(x)) 与 (g(x)) 均为积性函数 则以下函数也为积性函数:
(h(x) = f(x^p) \ h(x) = f^p(x) \ h(x) = f(x)g(x) \ h(x) = sum_{d mid x}f(d)g(frac xd))
常见的积性函数
-
单位函数 (e(n) = [n = 1])
-
幂函数 (id_k(n) = n^k) ((id_1(n)) 通常记为 (id(n)))
-
常数函数 (1(n) = 1)
-
因数函数 (d(n) = sum_{d mid n} 1)
-
除数函数 (sigma _k(n) = sum_{d mid n}d^k)
(k = 0) 时 为因数个数函数 (sigma_0(n))
(k = 1) 时 为因数和函数 (sigma(n))
-
欧拉函数 (varphi(n) = sum_{i = 1}^n[gcd(i, n) = 1])
-
莫比乌斯函数 (mu(n) = egin{cases}1 & n = 1 \ 0 & n 含有平方因子 \ (-1)^k & k 为 n 的本质不同质因子个数 end{cases})
比较假
莫比乌斯函数
定义 : (mu(n) = egin{cases}1 & n = 1 \ 0 & n 含有平方因子 \ (-1)^k & k 为 n 的本质不同质因子个数 end{cases})
令 (n = prod_{i = 1}^kp_i^{c_i}) 其中 (p_i) 为质因子
-
(n = 1) 时 (mu(n) = 1)
-
$ n e 1$ 时
(exists i in [1, k], c_i > 1) 时 (mu(n) = 0)
即当某质因子出现次数大于 (1) 时 (mu(n) = 0)
(forall i in [1, k], c_i = 1) 时 (mu(n) = (-1)^k)
即当每个质因子只出现一次时 (mu(n) = (-1)^k) 此处 (k) 为质因子的种类数
性质
(sum_{d mid n}mu(n) = [n = 1])
证明没看懂
结论
([gcd(i, j) = 1] Longleftrightarrow sum_{d mid gcd(i, j)}mu(d))
线筛
因为是积性函数 所以可以线筛
先咕了 学莫比乌斯反演的时候再补(看到再补就大概率不补了)
狄利克雷卷积
太假了 人没了
学不会 弃了
定义
定义两个数论函数 (f, g) 的狄利克雷卷积为 ((f * g)(n) = sum_{d mid n}f(d)g(frac nd))
性质
满足交换律 结合律 分配律
(e) 为狄利克雷卷积的单位元 有 ((f * e)(n) = f(n))
若 (f, g) 为积性函数 则 (f * g) 为积性函数
一些没学或者是学了没学会的东西
(a mod m) 的阶
原根
(FFT)
(NTT)
二次剩余
组合数取模
积性函数
莫比乌斯函数
狄利克雷卷积
反演
杜教筛
类欧几里得算法