数论
位运算
- $ a + b = a \& b + a | b $
- 二进制逆序
a = ((a & 0xAAAA) >> 1) | ((a & 0x5555) << 1);
a = ((a & 0xCCCC) >> 2) | ((a & 0x3333) << 2);
a = ((a & 0xF0F0) >> 4) | ((a & 0x0F0F) << 4);
a = ((a & 0xFF00) >> 8) | ((a & 0x00FF) << 8);
- 要求集合中不能有两个相邻的元素
if ((mask >> 1) & mask) continue;
- 在限定必须不取某些元素的前提下枚举子集
// mask的第x位为0表示x必须不在子集中(原集合中不含这个元素):
for (int mask1 = mask; mask1 >= 0; mask1 = (mask1 - 1) & mask)
- int __builtin_ffs(int x) :返回x的二进制末尾最后一个1的位置,位置的编号从1开始(最低位编号为1)。当x为0时返回0
- int __builtin_clz(unsigned int x) :返回x的二进制的前导0的个数。当x为0时,结果未定义。
- int __builtin_ctz(unsigned int x) :返回x的二进制末尾连续0的个数。当x为0时,结果未定义。
- int __builtin_clrsb(int x) :当x的符号位为0时返回x的二进制的前导0的个数减一,否则返回x的二进制的前导1的个数减一。
- int __builtin_popcount(unsigned int x) :返回x的二进制中1的个数。
- int __builtin_parity(unsigned int x) :判断x的二进制中1的个数的奇偶性。
这些函数都可以在函数名末尾添加 l 或 ll (如 builtin_popcountll )来使参数类型变为 ( unsigned ) long 或 ( unsigned ) long long (返回值仍然是 int 类型)。 例如,我们有时候希望求出一个数以二为底的对数,如果不考虑 0 的特殊情况,就相当于这个数二进制的位数 -1 ,而一个数 n 的二进制表示的位数可以使用 32-__builtin_clz(n) 表示,因此 31-builtin_clz(n) 就可以求出 n 以二为底的对数。
拓展欧几里得算法
求解$ ax + by = gcd(a, b) $
LL exgcd(LL a, LL b, LL &x, LL &y) {
LL d = a;
if (b == 0)
x = 1, y = 0;
else {
d = exgcd(b, a % b, y, x), y -= a / b * x;
}
return d;
}
基础快速幂
LL ksm(LL n, LL m, LL p = MOD) {
LL res = 1;
while (m > 0) {
if (m & 1) res = n * res % p;
m >>= 1;
n = n * n % p;
}
return res % p;
}
多幂数快速幂
对于给定x和n个幂数 $ a_i $ 和模数p,求 $ x^{a_i} \mod p $
解法:设 $ S = \sqrt p + 1 $ ,预处理S以内快速幂,那么 $ x^y = x^{y \mod S} * x^{\lfloor \frac{y}{S} \rfloor *S} $
快速乘
LL mul(LL a, LL b, LL mod) {
LL res = a * b - mod * (LL)((long double)a / mod * b);
if (res < 0) return res + mod;
if (res < mod) return res;
return res - mod;
}
素数判定
Pollard-Rho算法
int pri[] = {0, 19260817, 1145141923};
bool isprime(LL n) {
if (n < 2) return 0;
for (int i = 1; i <= 2; ++i)
if (pri[i] == n) return 1;
int cnt = 0;
LL res = n - 1;
while (!(res & 1)) cnt++, res >>= 1;
for (int i = 1; i <= 2; ++i) {
LL x = ksm(pri[i], res, n), t;
for (int j = 1; j <= cnt; ++j) {
t = mul(x, x, n);
if (t == 1 && x != 1 && x != n - 1) return 0;
x = t;
}
if (x != 1) return 0;
}
return 1;
}
LL random(LL n) { return 1LL * rand() * rand() * rand() * rand() % n; }
LL PoLLardRho(LL n) {
if (n == 4) return 2;
while (1) {
LL c = random(n - 1) + 1;
#define f(x) (mul(x, x, n) + c)
LL s = f(0), t = f(s), v = 1, cnt = 0, o = 1;
while (s != t) {
v = mul(v, abs(s - t), n);
if ((++cnt) % 114 == 0) {
LL d = __gcd(n, v);
if (d > 1 && d < n) return d;
}
if (cnt == o) {
LL d = __gcd(n, v);
if (d > 1 && d < n) return d;
v = 1, cnt = 0, o *= 2;
}
s = f(s), t = f(f(t));
}
LL d = __gcd(n, v);
if (d > 1 && d < n) return d;
}
}
void dfs(LL x, LL& ans) {
if (x < ans) return;
if (isprime(x)) return ans = max(ans, x), void();
LL d = PoLLardRho(x);
dfs(d, ans);
while (x % d == 0) x /= d;
dfs(x, ans);
}
int main() {
srand(time(0));
LL n, ans;
read(n);
ans = 2;
dfs(n, ans);
if (ans == n) puts("Prime");
}
筛法
线性筛
const LL p_max = 1E6 + 100;
LL pr[p_max], p_sz;
void get_prime() {
static bool vis[p_max];
for (int i = 2; i < p_max; ++i) {
if (!vis[i]) pr[p_sz++] = i;
for (int j = 0; j < p_sz; ++i) {
if (pr[j] * i >= p_max) break;
vis[pr[j] * i] = 1;
if (i % pr[j] == 0) break;
}
}
}
欧拉函数
求欧拉函数值
int euler_phi(int n) {
int ans = n;
for (int i = 2; i * i <= n; i++)
if (n % i == 0) {
ans = ans / i * (i - 1);
while (n % i == 0) n /= i;
}
if (n > 1) ans = ans / n * (n - 1);
return ans;
}
筛法求欧拉函数
void pre() {
memset(is_prime, 1, sizeof(is_prime));
int cnt = 0;
is_prime[1] = 0;
phi[1] = 1;
for (int i = 2; i <= 5000000; i++) {
if (is_prime[i]) {
prime[++cnt] = i;
phi[i] = i - 1;
}
for (int j = 1; j <= cnt && i * prime[j] <= 5000000; j++) {
is_prime[i * prime[j]] = 0;
if (i % prime[j])
phi[i * prime[j]] = phi[i] * phi[prime[j]];
else {
phi[i * prime[j]] = phi[i] * prime[j];
break;
}
}
}
}
裴蜀定理
设a, b是不全为零的整数,则存在整数x, y, 使得$ ax + by = gcd(a, b) $
费马小定理
若$ p $为素数,$ gcd(a, p) = 1 $,则$ a ^ {p - 1} \equiv 1 \pmod p $
另一个形式:对于任意整数$ a $,有$ a^p \equiv a \pmod p $
欧拉定理
若$ gcd(a, m) = 1 $,则$ a ^ {\phi (m)} \equiv 1 \pmod m $
拓展欧拉定理
威尔逊定理
对于素数$ p $, 有$ (p - 1)! \equiv -1 (\mod p) $
线性求逆元
inv[1] = 1;
for (int i = 2; i <= n; ++i) {
inv[i] = (LL)(p - p / i) * inv[p % i] % p;
}
线性求任意n个数的逆元(复杂度$ O(n + \log p) $)
s[0] = 1;
for (int i = 1; i <= n; ++i) s[i] = s[i - 1] * a[i] % p;
sv[n] = ksm(s[n], p - 2, p);
for (int i = n; i >= 1; --i) sv[i - 1] = sv[i] * a[i] % p;
for (int i = 1; i <= n; ++i) inv[i] = sv[i] * s[i - 1] % p;
中国剩余定理(余数为a,模数为r)
LL CRT(int k, LL* a, LL* r) {
LL n = 1, ans = 0;
for (int i = 1; i <= k; i++) n = n * r[i];
for (int i = 1; i <= k; i++) {
LL m = n / r[i], b, y;
exgcd(m, r[i], b, y);
ans = (ans + a[i] * m * b % mod) % mod;
}
return (ans % mod + mod) % mod;
}
二次剩余欧拉判别准则
$ \left(\frac{n}{p}\right)\equiv n^{\frac{p-1}{2}}\pmod p $
若n是二次剩余,当且仅当$ n^{\frac{p-1}{2}}\equiv 1\pmod p $
若n是非二次剩余,当且仅当$ n^{\frac{p-1}{2}}\equiv -1\pmod p $
BSGS
求$ a^x \equiv b \pmod p $
LL BSGS(LL a, LL b, LL p) {
a %= p;
if (!a && !b) return 1;
if (!a) return -1;
static map<LL, LL> mp;
mp.clear();
LL m = sqrt(p + 1.5);
LL v = 1;
for (int i = 1; i <= m; ++i) {
v = v * a % p;
mp[v * b % p] = i;
}
LL vv = v;
for (int i = 1; i <= m; ++i) {
auto it = mp.find(vv);
if (it != mp.end()) return i * m - it->second;
vv = vv * v % p;
}
return -1;
}
LL exBSGS(LL a, LL b, LL p) {
a %= p, b %= p;
if (a == 0) return b > 1 ? -1 : b == 0 && p != 1;
LL c = 0, q = 1;
while (1) {
LL g = __gcd(a, p);
if (g == 1) break;
if (b == 1) return c;
if (b % g) return -1;
++c;
b /= g, p /= g;
q = a / g * q % p;
}
static map<LL, LL> mp;
mp.clear();
LL m = sqrt(p + 1.5);
LL v = 1;
for(int i = 1; i <= m; ++i) {
v = v * a % p;
mp[v * b % p] = i;
}
for(int i = 1; i <= m; ++i) {
q = q * v % p;
auto it = mp.find(q);
if (it != mp.end()) return i * m - it->second + c;
}
return -1;
}
杜教筛求欧拉函数和及莫比乌斯函数前缀和
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <map>
using namespace std;
const int maxn = 2000010;
long long T, n, pri[maxn], cur, mu[maxn], sum_mu[maxn];
bool vis[maxn];
map<long long, long long> mp_mu;
long long S_mu(long long x) { //求mu的前缀和
if (x < maxn) return sum_mu[x];
if (mp_mu[x]) return mp_mu[x]; //如果map中已有该大小的mu值,则可直接返回
long long ret = (long long)1;
for (long long i = 2, j; i <= x; i = j + 1) {
j = x / (x / i);
ret -= S_mu(x / i) * (j - i + 1);
}
return mp_mu[x] = ret; //路径压缩,方便下次计算
}
long long S_phi(long long x) { //求phi的前缀和
long long ret = (long long)0;
long long j;
for (long long i = 1; i <= x; i = j + 1) {
j = x / (x / i);
ret += (S_mu(j) - S_mu(i - 1)) * (x / i) * (x / i);
}
return (ret - 1) / 2 + 1;
}
int main() {
scanf("%lld", &T);
mu[1] = 1;
for (int i = 2; i < maxn; i++) { //线性筛预处理mu数组
if (!vis[i]) {
pri[++cur] = i;
mu[i] = -1;
}
for (int j = 1; j <= cur && i * pri[j] < maxn; j++) {
vis[i * pri[j]] = true;
if (i % pri[j])
mu[i * pri[j]] = -mu[i];
else {
mu[i * pri[j]] = 0;
break;
}
}
}
for (int i = 1; i < maxn; i++)
sum_mu[i] = sum_mu[i - 1] + mu[i]; //求mu数组前缀和
while (T--) {
scanf("%lld", &n);
printf("%lld %lld\n", S_phi(n), S_mu(n));
}
return 0;
}
Min_25筛求素数和
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
const int N = 1000000 + 10;
int prime[N], id1[N], id2[N], flag[N], ncnt, m;
LL g[N], sum[N], a[N], T, n;
int ID(LL x) { return x <= T ? id1[x] : id2[n / x]; }
LL calc(LL x) { return x * (x + 1) / 2 - 1; }
LL init(LL x) {
T = sqrt(x + 0.5);
for (int i = 2; i <= T; i++) {
if (!flag[i]) prime[++ncnt] = i, sum[ncnt] = sum[ncnt - 1] + i;
for (int j = 1; j <= ncnt && i * prime[j] <= T; j++) {
flag[i * prime[j]] = 1;
if (i % prime[j] == 0) break;
}
}
for (LL l = 1; l <= x; l = x / (x / l) + 1) {
a[++m] = x / l;
if (a[m] <= T)
id1[a[m]] = m;
else
id2[x / a[m]] = m;
g[m] = calc(a[m]);
}
for (int i = 1; i <= ncnt; i++)
for (int j = 1; j <= m && (LL)prime[i] * prime[i] <= a[j]; j++)
g[j] = g[j] - (LL)prime[i] * (g[ID(a[j] / prime[i])] - sum[i - 1]);
}
LL solve(LL x) {
if (x <= 1) return x;
return n = x, init(n), g[ID(n)];
}
int main() {
memset(g, 0, sizeof(g));
memset(a, 0, sizeof(a));
memset(sum, 0, sizeof(sum));
memset(prime, 0, sizeof(prime));
memset(id1, 0, sizeof(id1));
memset(id2, 0, sizeof(id2));
memset(flag, 0, sizeof(flag));
ncnt = m = 0;
scanf("%LLd", &n);
printf("%LLd\n", solve(n));
}
Comments NOTHING