bzoj3309:DZY Loves Math

Pre

又是牛顿。

原来线性筛也是可以癌变成为毒瘤的。

。。。

我也不吐槽什么了。(图片有改动,但是大体没变)

Solution

式子推完之后,又是一个线性筛,要筛的是。

\(F(n)=\sum\limits_{d|n}\mu(d)*f(\frac{n}{d})\)

\(n\)唯一分解。

\(n=\prod\limits_{i=1}^{k}p_i^{a_i}\)

可以证明,如果\(a_i\neq 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\neq 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\n", 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

注意有些时候前缀和的计算要在循环外面进行,不然会在回溯寻找信息的时候找到错误信息(也可以在回溯的时候通过前缀和进行计算)。

线性筛感觉跟没学一样。

线性筛好像有挺大的可拓展性。

猜你喜欢

转载自www.cnblogs.com/ChiTongZ/p/11166638.html