Pre
又是牛顿。
原来线性筛也是可以癌变成为毒瘤的。
我也不吐槽什么了。(图片有改动,但是大体没变)
Solution
式子推完之后,又是一个线性筛,要筛的是。
(F(n)=sumlimits_{d|n}mu(d)*f(frac{n}{d}))
对(n)唯一分解。
(n=prodlimits_{i=1}^{k}p_i^{a_i})
可以证明,如果(a_i eq a_j)
则(F(n)=0)
证明:
假设显然有(f(frac{n}{d})=max(a_i)或max(a_i)-1)
否则莫比乌斯函数的值为0
从所有的(a_i=max(a_i))的集合中的选择,都可以在(a_i<max(a_i))的集合中找奇偶组数相等。
这一步是由二项式展开得到的。
但是展开的要求是集合非空。
在这道题中就是存在(a_i eq a_j)。
伪证毕(网上的证明没有看懂,所以我自己又写了一篇可能也看不懂的证明)。
剩下的就好办了。
除了线性筛。
所以我们要筛的是(F(n))
我的代码里面
(f[])表示函数值
(g[])表示(p_i)最小的(p_i^{a_i})
(s[])表示(a_i)
如果对线性筛极为熟练(就是比我领先一大截的那种),应该很好明白。
线性筛的性质是一个数被最小的因子筛掉(废话)。
Code
#include <bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define xx first
#define yy second
#define mod 100000009
using namespace std;
const int N = 10000000 + 5, M = 10000000;
bool vis[N];
int pri[N], tot;
ll f[N], g[N], s[N];
inline void init ();
int main () {
init ();
int t;
scanf ("%d", &t);
while (t--) {
int a, b;
scanf ("%d%d", &a, &b);
if (a < b) {
swap (a, b);
}
ll l = 1, r;
ll res = 0;
while (1) {
if (l > b) {
break;
}
r = min (a / (a / l), b / (b / l));
res += 1LL * (a / l) * (b / l) * (f[r] - f[l - 1]);
l = r + 1;
}
printf ("%lld
", res);
}
return 0;
}
inline void init () {
for (int i = 2; i <= M; ++i) {
if (!vis[i]) {
pri[++tot] = i;
f[i] = 1;
g[i] = i;
s[i] = 1;
}
for (int j = 1; j <= tot; ++j) {
if (1LL * pri[j] * i > M) {
break;
}
vis[pri[j] * i] = 1;
if (i % pri[j] == 0) {
s[pri[j] * i] = s[i] + 1;
g[pri[j] * i] = g[i] * pri[j];
if (pri[j] * i == g[pri[j] * i]) {
f[pri[j] * i] = 1;
}
else {
f[pri[j] * i] = s[i * pri[j] / g[i * pri[j]]] == s[i * pri[j]] ? -f[i * pri[j] / g[i * pri[j]]] : 0;
}
break;
}
else {
f[pri[j] * i] = s[i] == 1 ? -f[i] : 0;
g[pri[j] * i] = pri[j];
s[pri[j] * i] = 1;
}
}
}
for (int i = 1; i <= M; ++i) {
f[i] += f[i - 1];
}
}
Conclusion
注意有些时候前缀和的计算要在循环外面进行,不然会在回溯寻找信息的时候找到错误信息(也可以在回溯的时候通过前缀和进行计算)。
线性筛感觉跟没学一样。
线性筛好像有挺大的可拓展性。