莫反学习笔记
数竞朋友们一致认为反演是几何的。
下文中的函数默认为积性函数。
本质是
一般会用到的:若
P3455 & P2522
后者只是在前者的基础上加入容斥,这里讲前者。
原问即求
首先先把
接着把
把
把
把后面化简一下。
线性筛后整除分块计算即可。
时间复杂度
const int N = 5e4 + 5;
int cnt, p[N], pri[N], mo[N];
void sieve()
{
mo[1] = 1;
for (int i = 2; i < N; i++)
{
if (!p[i])
{
p[i] = i;
pri[++cnt] = i;
mo[i] = -1ll;
}
for (int j = 1; j <= cnt; j++)
{
if (i * pri[j] > N) break;
p[i * pri[j]] = j;
if (i % pri[j] == 0)
{
mo[i * pri[j]] = 0;
break;
}
mo[i * pri[j]] = -mo[i];
}
}
for (int i = 1; i < N; i++) mo[i] = mo[i - 1] + mo[i];
}
int t, a, b, d;
int solve(int n, int m)
{
int ret = 0;
for (int i = 1, j; i <= min(n, m); i = j + 1)
{
j = min(n / (n / i), m / (m / i));
ret += (mo[j] - mo[i-1]) * (m / i) * (n / i);
}
return ret;
}
int main()
{
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
sieve();
cin >> t;
while (t--)
{
cin >> a >> b >> d;
cout << solve(a / d, b / d) << "\n";
}
return 0;
}
SP5971
直接看吧。
易知
设
打包一下,得到
推一下柿子,可知
本题不是莫反,但是可以练习推柿子。
const int N = 1e6 + 5;
int cnt, p[N], pri[N];
ll g[N];
void sieve()
{
g[1] = 1;
for (int i = 2; i < N; i++)
{
if (!p[i])
{
p[i] = i;
pri[++cnt] = i;
g[i] = 1ll * i * (i - 1) + 1;
}
for (int j = 1; j <= cnt; j++)
{
if (i * pri[j] > N) break;
p[i * pri[j]] = j;
if (i % pri[j] == 0)
{
g[i * pri[j]] = g[i] + (g[i] - g[i / pri[j]]) * pri[j] * pri[j];
break;
}
g[i * pri[j]] = g[i] * g[pri[j]];
}
}
}
int t, n;
int main()
{
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
sieve();
cin >> t;
while (t--)
{
cin >> n;
cout << (g[n] + 1ll) * n / 2 << "\n";
}
return 0;
}
P1829
求
首先转化,假定
考虑枚举最大公因数
把
把后半部分单独拉出来,记为
仿照 P3455 转化。
显然转化成
后面的一堆可以记为
这个东西就可以数论分块了。
带回原式即得
显然也可以数论分块。
最终复杂度是线性筛的
注意取模。
const int N = 1e7 + 5, mod = 20101009, inv2 = 10050505, inv4 = 15075757;
int cnt, p[N], pri[N], mo[N], sum[N];
void sieve()
{
mo[1] = 1;
for (int i = 2; i < N; i++)
{
if (!p[i])
{
p[i] = i;
pri[++cnt] = i;
mo[i] = -1ll;
}
for (int j = 1; j <= cnt; j++)
{
if (i * pri[j] > N) break;
p[i * pri[j]] = j;
if (i % pri[j] == 0)
{
mo[i * pri[j]] = 0;
break;
}
mo[i * pri[j]] = -mo[i];
}
}
for (int i = 1; i < N; i++)
sum[i] = (sum[i - 1] + 1ll * i * i % mod * (mo[i] + mod) % mod) % mod;
}
int n, m;
int s(int l, int r) {return 1ll * (l + r) * (r - l + 1) % mod * inv2 % mod;}
int g(int n, int m) {return 1ll * n * (n + 1) % mod * m % mod * (m + 1) % mod * inv4 % mod;}
int f(int n, int m)
{
int ret = 0;
for (int i = 1, j; i <= min(n, m); i = j + 1)
{
j = min(n / (n / i), m / (m / i));
ret = (ret + 1ll * (sum[j] - sum[i-1] + mod) % mod * g(n / i, m / i) % mod) % mod;
}
return ret;
}
int solve(int n, int m)
{
int ret = 0;
for (int i = 1, j; i <= min(n, m); i = j + 1)
{
j = min(n / (n / i), m / (m / i));
ret = (ret + 1ll * s(i, j) * f(n / i, m / i) % mod) % mod;
}
return ret;
}
int main()
{
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
sieve();
cin >> n >> m;
cout << solve(n, m) << "\n";
return 0;
}
P3312
题目即求
首先考虑没有
后面的部分就比较典了,就是例一的结果。
考虑令
做到这就可以在没有
考虑如何处理
记
每次询问用数论分块
考虑复杂度。
单次询问是
至于怎么筛
const int N = 1e5 + 5;
int cnt, p[N], pri[N], mo[N], g[N];
pair <int, int> sig[N];
void sieve()
{
mo[1] = g[1] = 1; sig[1] = {1, 1};
for (int i = 2; i < N; i++)
{
if (!p[i])
{
p[i] = i;
pri[++cnt] = i;
mo[i] = -1ll;
sig[i] = {i + 1, i};
g[i] = i + 1;
}
for (int j = 1; j <= cnt; j++)
{
if (i * pri[j] >= N) break;
p[i * pri[j]] = pri[j];
if (i % pri[j] == 0)
{
mo[i * pri[j]] = 0;
g[i * pri[j]] = g[i] * pri[j] + 1;
sig[i * pri[j]] = {sig[i].first / g[i] * g[i * pri[j]], i * pri[j]};
break;
}
mo[i * pri[j]] = -mo[i];
sig[i * pri[j]] = {sig[i].first * sig[pri[j]].first, i * pri[j]};
g[i * pri[j]] = pri[j] + 1;
}
}
}
struct _ {int n, m, a, id, ans;} s[N];
int q;
int f[N];
void modify(int x, int v) {for (; x < N; x += x & -x) f[x] += v;}
int query(int x) {int ret = 0; for (; x; x -= x & -x) ret += f[x]; return ret;}
int solve(int n, int m)
{
int ret = 0;
for (int i = 1, j; i <= min(n, m); i = j + 1)
{
j = min(n / (n / i), m / (m / i));
ret += (query(j) - query(i - 1)) * (n / i) * (m / i);
}
return ret;
}
int main()
{
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
cin >> q; sieve();
for (int i = 1; i <= q; i++) cin >> s[i].n >> s[i].m >> s[i].a, s[i].id = i;
sort(s + 1, s + q + 1, [](_ x, _ y){return x.a < y.a;});
sort(sig + 1, sig + N);
for (int i = 1, j = 1; i <= q; i++)
{
while (j < N && sig[j].first <= s[i].a)
{
int o = sig[j].second;
for (int p = o; p < N; p += o) modify(p, mo[p/o] * sig[j].first);
j++;
}
s[i].ans = solve(s[i].n, s[i].m);
}
sort(s + 1, s + q + 1, [](_ x, _ y){return x.id < y.id;});
for (int i = 1; i <= q; i++) cout << (s[i].ans & (~(1 << 31))) << "\n";
return 0;
}
P3704
题目即求
先转换一波,依然设
这时我们仿照例四,设
内层可以用调和级数复杂度预处理,外层数论分块即可。
时间复杂度
const int N = 1e6 + 5, mod = 1e9 + 7;
int cnt, p[N], pri[N], mo[N], f[N], _f[N], F[N];
void add(int &x, int y) {x += y; if (x >= mod) x -= mod;}
void mul(int &x, int y) {x = 1ll * x * y % mod;}
int ksm(int a, int b)
{
int ret = 1;
while (b)
{
if (b & 1) mul(ret, a);
mul(a, a);
b >>= 1;
}
return ret;
}
int inv(int x) {return ksm(x, mod - 2);}
void sieve()
{
mo[1] = 1; F[0] = F[1] = f[1] = _f[1] = 1;
for (int i = 2; i < N; i++)
{
f[i] = (f[i-1] + f[i-2]) % mod; _f[i] = inv(f[i]);
F[i] = 1;
if (!p[i])
{
p[i] = i;
pri[++cnt] = i;
mo[i] = -1ll;
}
for (int j = 1; j <= cnt; j++)
{
if (i * pri[j] >= N) break;
p[i * pri[j]] = pri[j];
if (i % pri[j] == 0)
{
mo[i * pri[j]] = 0;
break;
}
mo[i * pri[j]] = -mo[i];
}
}
for (int i = 2; i < N; i++)
for (int j = i; j < N; j += i)
mul(F[j], (mo[j / i] == 1 ? f[i] : (mo[j / i] == -1 ? _f[i] : 1)));
for (int i = 2; i < N; i++) mul(F[i], F[i-1]);
}
int t, n, m;
int solve(int n, int m)
{
int ret = 1;
for (int i = 1, j; i <= min(n, m); i = j + 1)
{
j = min(n / (n / i), m / (m / i));
mul(ret, ksm(1ll * F[j] * inv(F[i-1]) % mod, 1ll * (n / i) * (m / i) % (mod - 1)));
}
return ret;
}
int main()
{
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
cin >> t; sieve();
while (t--)
{
cin >> n >> m;
cout << solve(n, m) << "\n";
}
return 0;
}
P3172
把
记
显然只有当
易知
整除分块即可,
吗?
可以发现,
那就杜教筛。复杂度
const int N = 5e6 + 5, mod = 1e9 + 7;
typedef long long ll;
ll cnt, p[N], pri[N], mo[N], mosum[N];
void sieve()
{
mo[1] = 1;
for (int i = 2; i < N; i++)
{
if (!p[i])
{
p[i] = i;
pri[++cnt] = i;
mo[i] = -1ll;
}
for (int j = 1ll; j <= cnt; j++)
{
if (i * pri[j] > N) break;
p[i * pri[j]] = j;
if (i % pri[j] == 0ll)
{
mo[i * pri[j]] = 0ll;
break;
}
mo[i * pri[j]] = -mo[i];
}
}
for (int i = 1; i < N; i++) mosum[i] = mosum[i - 1] + mo[i];
}
unordered_map <int, int> m;
ll cmo(ll x)
{
if (x < N) return mosum[x];
if (m.count(x)) return m[x];
ll l, r;
ll ans = 1;
for (l = 2ll; l <= x; l = r + 1ll)
{
r = x / (x / l);
ans -= (r - l + 1ll) * cmo(x / l);
}
m[x] = ans;
return ans;
}
int n, k, a, b;
int ksm(int a, int b)
{
int ret = 1;
while (b)
{
if (b & 1) ret = 1ll * ret * a % mod;
a = 1ll * a * a % mod;
b >>= 1;
}
return ret;
}
int solve(int a, int b)
{
int ret = 0;
for (int i = 1, j; i <= b; i = j + 1)
{
if (a < i) j = (b / (b / i));
else j = min(a / (a / i), b / (b / i));
ret = (ret + 1ll * ksm(b / i - a / i, n) * (cmo(j) - cmo(i - 1) + mod) % mod) % mod;
}
return ret;
}
int main()
{
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
cin >> n >> k >> a >> b; sieve();
b /= k; a = (a + k - 1) / k; a--;
cout << solve(a, b);
return 0;
}
QwQ。