持续更新
A - 乘法逆元
求 (1∼n) 中的所有数在模 (p) 意义下的乘法逆元
1.快速幂
根据欧拉定理,若(a)和(p)互质,则(a^{varphi(p)}equiv1;(mod;p)),当(p)是质数,则满足:
(p) 为质数, (a) 为正整数, (a) 和 (p) 互质, 则(a^{p-1} equiv 1;(mod;p)).
根据费马小定理: (a imes a^{p-2} equiv 1;(mod ; p)), (a^{p-2};(mod;p))即为(a)的逆元, 利用快速幂求解即可.
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
int n, p;
int pow_mod(int a, int b, int p)
{
int res = 1;
while(b)
{
if(b & 1) res = (LL)res * a % p;
a = (LL)a * a % p;
b >>= 1;
}
return res;
}
int main()
{
scanf("%d%d", &n, &p);
for(int i = 1; i <= n; ++ i)
printf("%d
", pow_mod(i, p - 2, p));
return 0;
}
2.扩展欧几里得
当(a)和(p)互质,但(p)不是质数时,解线性同余方程(a*xequiv1;(mod;p)):
转化为:(a*x+p*yequiv1),因为(a)和(p)互质,所以一定有解,用exgcd求解即可
exgcd解出的逆元有可能是负数,转化成(mod;p)意义下的正数即可
#include <bits/stdc++.h>
using namespace std;
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()
{
int n, p;
scanf("%d%d", &n, &p);
for(int i = 1; i <= n; ++ i)
{
int x, y;
exgcd(i, p, x, y);
x = (x % p + p) % p;
printf("%d
", x);
}
return 0;
}
3.线性递推
设(p = k * i + r), (k)是(dfrac{p}{i})的商,(r)是余数
(k*i+requiv0;(mod;p)), 两边同时乘上(i^{-1})和(r^{-1})
(k * r^{-1} + i^{-1} equiv 0;(mod;p))
(i^{-1} equiv -k * r^{-1};(mod;p))
(i^{-1} equiv -leftlfloordfrac{p}{i} ight floor * (p;mod;i)^{-1};(mod;p))
且已知(1^{-1}equiv1;(mod;p)),则可以线性递推
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 3e6 + 20;
int inv[N];
int main()
{
int n, p;
scanf("%d%d", &n, &p);
inv[1] = 1; puts("1");
for(int i = 2; i <= n; ++ i)
{
inv[i] = (LL)(p - p / i) * inv[p % i] % p;
printf("%d
", inv[i]);
}
return 0;
}
B - 除数函数求和 1
(sigma_k(n) = sum_{d|n}d^k), 求(sum_{i=1}^nsigma_k(i))的值对(10^9 + 7)取模的结果.
考虑每个因子(d)对答案的贡献,即(sumlimits_{i = 1}^n i^k * leftlfloordfrac{n}{i} ight floor)
然后暴力处理(i^k),复杂度(O(nlogk))
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int P = 1e9 + 7;
int pow_mod(int a, int b, int p)
{
int res = 1;
while(b)
{
if(b & 1) res = (LL)res * a % p;
a = (LL)a * a % p;
b >>= 1;
}
return res;
}
int main()
{
int n, k;
scanf("%d%d", &n, &k);
int res = 0;
for(int i = 1; i <= n; ++ i)
res = (res + (LL)n / i * pow_mod(i, k, P) % P) % P;
printf("%d", res);
return 0;
}
考虑继续优化:在求幂时有重复计算,因为每个数都可以唯一分解,所以只需要计算质因子的幂即可
考虑线性筛,用每个数的质因子的幂去计算它的幂.
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int P = 1e9 + 7;
const int N = 1e7 + 10;
bool st[N];
int primes[N], cnt;
int mi[N];
int pow_mod(int a, int b, int p)
{
int res = 1;
while(b)
{
if(b & 1) res = (LL)res * a % p;
a = (LL)a * a % p;
b >>= 1;
}
return res;
}
void init(int n, int k)
{
mi[1] = 1;
for(int i = 2; i <= n; ++ i)
{
if(!st[i])
{
primes[cnt ++ ] = i;
mi[i] = pow_mod(i, k, P);
}
for(int j = 0; primes[j] * i <= n; ++ j)
{
st[primes[j] * i] = 1;
mi[primes[j] * i] = (LL)mi[i] * mi[primes[j]] % P;
if(i % primes[j] == 0) break;
}
}
}
int main()
{
int n, k;
scanf("%d%d", &n, &k);
init(n, k);
int res = 0;
for(int i = 1; i <= n; ++ i)
res = (res + (LL)n / i * mi[i] % P) % P;
printf("%d
", res);
return 0;
}
C - 除数函数求和 2
(sigma_k(i) = sum_{d|i}d^k)
求(sum_{i=1}^n2sigma_2(i)+3sigma_1(i)+5sigma_0(i))对(998244353)取模.
和上题一样考虑每个因子的贡献,转化为求(sumlimits_{i=1}^n(2i^2+3i+5) * leftlfloordfrac{n}{i} ight floor)
(n)的范围在(10^9)内,考虑整除分块
转化为(2sumlimits_{i=1}^ni^2leftlfloordfrac{n}{i} ight floor + 3sumlimits_{i=1}^nileftlfloordfrac{n}{i} ight floor + 5sumlimits_{i=1}^nleftlfloordfrac{n}{i} ight floor)
且已知:
(sumlimits_{i=1}^n1=n)
(sumlimits_{i=1}^ni=dfrac{(1+n)*n}{2})
(sumlimits_{i=1}^ni^2=dfrac{n*(n+1)*(2n+1)}{6})
注:运算都是在(mod;p)意义下的,故除法要乘逆元;且做减法后可能会出现负数,转化为(mod;p)意义下的正数
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int P = 998244353;
const int inv3 = 332748118;
const int inv2 = 499122177;
int g(int k, int x)
{
return k / (k / x);
}
int f2(int l, int r)
{
int res1 = 1, res2 = 1;
l --;
res1 = (LL)res1 * l % P;
res1 = (LL)res1 * (l + 1) % P;
res1 = (LL)res1 * (2 * l + 1) % P;
res2 = (LL)res2 * r % P;
res2 = (LL)res2 * (r + 1) % P;
res2 = (LL)res2 * (2 * r + 1) % P;
int res = res2 - res1;
res = (res % P + P) % P;
res = (LL)res * inv3 % P;
return res;
}
int f1(int l, int r)
{
int res = 1;
res = (LL)res * (l + r) % P;
res = (LL)res * (r - l + 1) % P;
res = (LL)res * 3 % P;
res = (LL)res * inv2 % P;
return res;
}
int f0(int l, int r)
{
int res = 1;
res = (LL)res * (r - l + 1) % P;
res = (LL)res * 5 % P;
return res;
}
int main()
{
int n;
scanf("%d", &n);
int res = 0;
for(int l = 1, r; l <= n; l = r + 1)
{
r = min(n, g(n, l));
res = (res + (LL)f0(l, r) * (n / l) % P) % P;
res = (res + (LL)f1(l, r) * (n / l) % P) % P;
res = (res + (LL)f2(l, r) * (n / l) % P) % P;
}
printf("%d
", res);
return 0;
}
D - 质数判定
判定输入的数是不是质数。
Miller_Rabin判质数,要用__int128或者快速乘
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef long double LD;
/*
LL mul(LL a, LL b, LL p)
{
__int128 x = a, y = b, m = p;
return (LL)(x * y % m);
}
*/
LL mul(ULL a, ULL b, LL p)
{
return (a * b - (ULL)((LD)a / p * b) * p + p) % p;
}
/*
LL mul(LL a, LL b, LL p)
{
LL res = 0;
while(b)
{
if(b & 1) res = (res + a) % p;
a = a * 2 % p;
b >>= 1;
}
return res;
}
*/
LL power(LL a, LL b, LL p)
{
LL res = 1;
while(b)
{
if(b & 1) res = mul(res, a, p);
a = mul(a, a, p);
b >>= 1;
}
return res;
}
LL p[20] = {0, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37};
bool Miller_Rabin(LL n)
{
if(n == 1) return 0;
for(int i = 1; i <= 9; ++ i)
{
if(p[i] == n) return 1;
else if(p[i] > n) return 0;
LL sum = power(p[i], n - 1, n), x = n - 1;
if(sum != 1) return 0;
while(sum == 1 && !(x % 2))
{
x >>= 1;
sum = power(p[i], x, n);
if(sum != 1 && sum != n - 1) return 0;
}
}
return 1;
}
int main()
{
LL n;
while(scanf("%lld", &n) == 1)
{
puts(Miller_Rabin(n) ? "Y" : "N");
}
return 0;
}
E - 乘法逆元 2
求(sum_{i=1}^na_i^{-1} imes998244353^{n-i};(mod;p))
(1 le n le 5000000,;1le a_i < p,; p=10^9 + 7)
解决此题之前先考虑如何(O(n))求阶乘的逆元
考虑如下递推式:
令 (infact[n]) 为 (n!)的逆元
(infact[i + 1] = dfrac{1}{(i + 1)!})
(infact[i + 1] * (i + 1) = dfrac{1}{i!} = infact[i])
故我们可以求出(n!)的逆元,然后逆推,得到(1...n!)所有的逆元
同理,还可以用(infact[i] * (i - 1)! = i^{-1}),(O(n))得到所有(i)的逆元
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 3e6 + 20;
int fact[N], infact[N];
int pow_mod(int a, int b, int p)
{
int res = 1;
while(b)
{
if(b & 1) res = (LL)res * a % p;
a = (LL)a * a % p;
b >>= 1;
}
return res;
}
int main()
{
int n, p;
scanf("%d%d", &n, &p);
fact[0] = 1;
for(int i = 1; i <= n; ++ i) fact[i] = (LL)fact[i - 1] * i % p;
infact[n] = pow_mod(fact[n], p - 2, p);
for(int i = n - 1; i >= 1; -- i)
infact[i] = (LL)infact[i + 1] * (i + 1) % p;
for(int i = 1; i <= n; ++ i)
{
int res = (LL)infact[i] * fact[i - 1] % p;
printf("%d
", res);
}
return 0;
}
本题可以考虑相同的思路
做前缀积 (fact[n] = prodlimits_{i=1}^na_i)
求出(infact[n]),用(infact[i + 1] * a_{i + 1} = infact[i])递推所有前缀积的逆元
然后用(infact[i] * fact[i - 1] = a_i^{-1}),(O(n))得到所有(a_i)的逆元
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int P = 1e9 + 7;
const int M = 998244353;
const int N = 5e6 + 20;
int a[N];
int fact[N], infact[N];
int pow_mod(int a, int b, int p)
{
int res = 1;
while(b)
{
if(b & 1) res = (LL)res * a % p;
a = (LL)a * a % p;
b >>= 1;
}
return res;
}
int main()
{
int n;
scanf("%d", &n);
for(int i = 1; i <= n; ++ i) scanf("%d", &a[i]);
fact[0] = 1;
for(int i = 1; i <= n; ++ i) fact[i] = (LL)fact[i - 1] * a[i] % P;
infact[n] = pow_mod(fact[n], P - 2, P);
for(int i = n - 1; i >= 1; -- i)
infact[i] = (LL)infact[i + 1] * a[i + 1] % P;
int ans = 0;
for(int i = 1; i <= n; ++ i)
{
int res = (LL)infact[i] * fact[i - 1] % P;
ans = (LL)ans * M % P;
ans = ((LL)ans + res) % P;
}
printf("%d
", ans);
return 0;
}
F - 快速幂 2
求(x^{a_i};(mod;p))
(1 le n le 5 imes 10^6,;1le x,a_i < p,; p = 998244352)
直接暴力快速幂的复杂度(O(nlogp))
考虑分块预处理
(y = leftlfloordfrac{y}{k} ight floor cdot k + y ;mod; k)
(x^y = x^{y;mod;k} cdot x^{leftlfloordfrac{y}{k} ight floor cdot k})
预处理复杂度(O(k + dfrac{p}{k})), 当(k) 取 (sqrt p + 1)时最优
预处理后即可(O(1))回答每个询问
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 5e6 + 20;
const int P = 998244352;
int a[N], x_i[N], x_ki[N];
int x, n;
int main()
{
scanf("%d%d", &x, &n);
int k = sqrt(P) + 1;
x_i[0] = 1;
for(int i = 1; i <= k; ++ i) x_i[i] = (LL)x_i[i - 1] * x % P;
x_ki[0] = 1;
for(int i = 1; i <= P / k; ++ i) x_ki[i] = (LL)x_ki[i - 1] * x_i[k] % P;
for(int i = 1; i <= n; ++ i)
{
int y;
scanf("%d", &y);
int res = (LL)x_i[y % k] * x_ki[y / k] % P;
printf("%d ", res);
}
puts("");
return 0;
}
G - 二项卷积
H - 模立方根
I - ZQC 的作业
J - Humble Numbers
求第(n)个质因子只有(2,3,5,7)的数
#include <bits/stdc++.h>
using namespace std;
const int N = 6000;
int f[N];
void init()
{
f[1] = 1;
int a = 1, b = 1, c = 1, d = 1;
for(int i = 2; i <= 5842; ++ i)
{
f[i] = min(min(f[a] * 2, f[b] * 3), min(f[c] * 5, f[d] * 7));
if(f[i] == f[a] * 2) a ++;
if(f[i] == f[b] * 3) b ++;
if(f[i] == f[c] * 5) c ++;
if(f[i] == f[d] * 7) d ++;
}
}
int main()
{
init();
int n;
while(scanf("%d", &n) == 1 && n)
{
if(n % 10 == 1 && n % 100 != 11) printf("The %dst humble number is %d.
", n, f[n]);
else if(n % 10 == 2 && n % 100 != 12) printf("The %dnd humble number is %d.
", n, f[n]);
else if(n % 10 == 3 && n % 100 != 13) printf("The %drd humble number is %d.
", n, f[n]);
else printf("The %dth humble number is %d.
", n, f[n]);
}
return 0;
}
K - X问题
求在小于等于(N)的正整数中有多少个(X)满足:
(X;mod;a_0=b_0,;X;mod;a_1=b_1,;X;mod;a_2=b_2,;…,;X;mod;a_i=b_i,;…;(0 < a_i le 10))
扩展中国剩余定理, 去掉了模数两两互质的条件
先从两个方程入手
(egin{cases}x = k_1a_1 + m_1\x = k_2a_2 + m_2end{cases})
(k_1a_1 + m_1 = k_2a_2 + m_2)
(k_1a_1 - k_2a_2 = m_2 - m_1)
若((a_1,a_2);|;(m_2 - m_1)),则有解
通解(egin{cases}k_1 + kdfrac{a_2}{d}\k_2 + kdfrac{a_1}{d}end{cases})
(x = k_1a_1 + m_1 = (k_1 + kdfrac{a_2}{d})a_1 + m_1)
(x = a_1k_1 + m_1 + k[a_1, a_2])
令(a_1k_1 + m_1 = m), ([a_1, a_2] = a)
(x = m + ka)
则将两个不定方程合并成了一个,合并(n - 1)次后只剩一个方程
即为 (x equiv m;(mod;a))
最小正整数解即为(m;mod;a)的正余数,然后即可求出小于等于(N)的正整数中(x)的个数.
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 10 + 2;
LL exgcd(LL a, LL b, LL &x, LL &y)
{
if(!b)
{
x = 1, y = 0;
return a;
}
LL d = exgcd(b, a % b, y, x);
y -= a / b * x;
return d;
}
LL a[N], b[N];
int main()
{
int __;
scanf("%d", &__);
while(__ -- )
{
LL n, m;
scanf("%lld%lld", &m, &n);
for(int i = 1; i <= n; ++ i) scanf("%lld", &a[i]);
for(int i = 1; i <= n; ++ i) scanf("%lld", &b[i]);
LL a1 = a[1], m1 = b[1];
bool flag = 0;
for(int i = 2; i <= n; ++ i)
{
LL a2 = a[i], m2 = b[i];
LL k1, k2;
LL d = exgcd(a1, a2, k1, k2);
if((m2 - m1) % d) { flag = 1; break; }
k1 *= (m2 - m1) / d;
LL t = a2 / d;
k1 = (k1 % t + t) % t;
m1 = a1 * k1 + m1;
a1 = abs(a1 / d * a2);
}
if(flag) puts("0");
else
{
LL res = (m1 % a1 + a1) % a1;
if(res > m) puts("0");
else if(res == 0) printf("%lld
", (m - res) / a1);
else printf("%lld
", (m - res) / a1 + 1);
}
}
return 0;
}
L - GCD
求满足(a le x le b,;c le y le d,;gcd(x, y) = k)的((x,y))数量.
且(5,7)和(7,5)被认为是相同的.
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e5 + 20;
int primes[N], cnt, mu[N], sum[N];
bool st[N];
void init()
{
mu[1] = 1;
for(int i = 2; i < N; ++ i)
{
if(!st[i])
{
primes[cnt ++ ] = i;
mu[i] = -1;
}
for(int j = 0; primes[j] * i < N; ++ j)
{
st[primes[j] * i] = 1;
if(i % primes[j] == 0) break;
mu[primes[j] * i] = -mu[i];
}
}
for(int i = 1; i < N; ++ i) sum[i] = sum[i - 1] + mu[i];
}
int g(int k, int x)
{
return k / (k / x);
}
LL f(int a, int b, int k)
{
LL res1 = 0, res2 = 0;
a = a / k, b = b / k;
int n = min(a, b);
for(int l = 1, r; l <= n; l = r + 1)
{
r = min(n, min(g(a, l), g(b, l)));
res1 += (LL)(sum[r] - sum[l - 1]) * (a / l) * (b / l);
}
for(int l = 1, r; l <= n; l = r + 1)
{
r = min(n, g(n, l));
res2 += (LL)(sum[r] - sum[l - 1]) * (n / l) * (n / l);
}
return res1 - res2 / 2;
}
int main()
{
init();
int __, cnt = 0;
scanf("%d", &__);
while(__ -- )
{
int a, b, c, d, k;
scanf("%d%d%d%d%d", &a, &b, &c, &d, &k);
if(k == 0) { printf("Case %d: 0
", ++ cnt); continue; }
LL res = f(b, d, k) - f(a - 1, d, k) - f(b, c - 1, k) + f(a - 1, c - 1, k);
printf("Case %d: %lld
", ++ cnt,res);
}
return 0;
}
M - GCD Again
#include <bits/stdc++.h>
using namespace std;
int main()
{
int n;
while(scanf("%d", &n) == 1 && n)
{
int res = n, m = n;
for(int i = 2; i <= n / i; ++ i)
{
if(n % i == 0)
{
while(n % i == 0) n /= i;
res = res / i * (i - 1);
}
}
if(n > 1) res = res / n * (n - 1);
printf("%d
",m - res - 1);
}
return 0;
}
N - The Euler function
求(sumlimits_{i=a}^bφ(i))
欧拉函数是积性函数,用线性筛预处理即可
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 3e6 + 20;
int primes[N], cnt;
int phi[N];
bool st[N];
void get_euler(int n)
{
phi[1] = 1;
for(int i = 2; i <= n; ++ i)
{
if(!st[i])
{
phi[i] = i - 1;
primes[cnt ++] = i;
}
for(int j = 0; primes[j] <= n / i; ++ j)
{
st[primes[j] * i] = 1;
if(i % primes[j] == 0)
{
phi[i * primes[j]] = phi[i] * primes[j];
break;
}
phi[primes[j] * i] = phi[i] * (primes[j] - 1);
}
}
}
int main()
{
get_euler(N - 1);
int a, b;
while(scanf("%d%d", &a, &b) == 2)
{
LL res = 0;
for(int i = a; i <= b; ++ i)
res += phi[i];
printf("%lld
", res);
}
return 0;
}
O - Fansblog
P - Discrete Logging
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const int N = 2e5 + 3;
const int null = 0x3f3f3f3f;
typedef long long LL;
int Hash[N], h[N];
int find(int x)
{
int k = (x % N + N) % N;
while(Hash[k] != null && Hash[k] != x)
{
k ++;
if(k == N) k = 0;
}
return k;
}
bool count(int x)
{
int k = (x % N + N) % N;
while(1)
{
if(Hash[k] == null) return 0;
if(Hash[k] == x) return 1;
k ++;
if(k == N) k = 0;
}
return 0;
}
int bsgs(int a, int b, int p)
{
memset(Hash, 0x3f, sizeof Hash);
if(1 % p == b % p) return 0;
int k = sqrt(p) + 1;
for(int i = 0, j = b % p; i < k; ++ i)
{
int k = find(j);
Hash[k] = j;
h[k] = i;
j = (LL)j * a % p;
}
int ak = 1;
for(int i = 0; i < k; ++ i) ak = (LL)ak * a % p;
for(int i = 1, j = ak % p; i <= k; ++ i)
{
if(count(j)) return (LL)i * k - h[find(j)];
j = (LL)j * ak % p;
}
return -1;
}
int main()
{
int a, p, b;
while(scanf("%d%d%d", &p, &a, &b) == 3)
{
if(a == 0 && p == 0 && b == 0) break;
int res = bsgs(a, b, p);
if(res == -1) puts("no solution");
else printf("%d
", res);
}
return 0;
}
Q - The Embarrassed Cryptographer
R - Minimum Sum LCM
S - GCD - Extreme (I)
T - Colossal Fibonacci Numbers!
U - One Friend at a Time
V - X^A Mod P
W - 乘法逆元
(M)和(N)互质,求满足K * M % N = 1的最小正整数K.
同A题,扩展欧几里得求逆元
#include <bits/stdc++.h>
using namespace std;
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()
{
int n, m;
scanf("%d%d", &m, &n);
int x, y;
int d = exgcd(m, n, x, y);
printf("%d
", (x % n + n) % n);
return 0;
}
X - 中国剩余定理
一个正整数(K),给出(K;Mod;)一些质数的结果,求符合条件的最小的(K)。
中国剩余定理
(egin{cases}x equiv a_1;(mod;m_1)\x equiv a_2;(mod;m_2)\...\x equiv a_k;(mod;m_k)end{cases})
(M = m_1m_2...m_k)
(M_i = dfrac{M}{m_i})
(M_i^{-1})表示(M_i)在(mod;m_i)条件下的逆元
则(x = a_1M_1M_1^{-1} + a_2M_2M_2^{-1} + ... + a_kM_kM_k^{-1})
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 10 + 2;
int n;
int A[N], B[N];
void exgcd(LL a, LL b, LL &x, LL &y)
{
if(!b)
{
x = 1, y = 0;
return;
}
exgcd(b, a % b, y, x);
y -= a / b * x;
}
int main()
{
scanf("%d", &n);
LL M = 1;
for(int i = 0; i < n; ++ i)
{
scanf("%d%d", &A[i], &B[i]);
M *= A[i];
}
LL res = 0;
for(int i = 0; i < n; ++ i)
{
LL Mi = M / A[i];
LL ti, y;
exgcd(Mi, A[i], ti, y);
res += B[i] * Mi * ti;
}
printf("%lld
", (res % M + M) % M);
return 0;
}