• bzoj3309:DZY Loves Math


    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

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

    线性筛感觉跟没学一样。

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

  • 相关阅读:
    错误: 找不到或无法加载主类/java BB.class 中文乱码
    如何新建git仓库并连接以及git branch相关命令
    String、StringBuffer与StringBuilder区别
    HTML选择器
    java中的system.out.println()和JSP中out.println()区别
    校园双选会,你都懂么
    android visible invisible和gone的区别
    Android 显示意图和隐式意图的区别
    android doGet和doPost
    Android 监听SMS短信
  • 原文地址:https://www.cnblogs.com/ChiTongZ/p/11166638.html
Copyright © 2020-2023  润新知