zoukankan      html  css  js  c++  java
  • Min_25筛学习笔记

    引入问题:求一个积性函数(f(i))的前缀和

    [sum_{i=1}^nf(i) ]

    其中(f(i))满足对质数(p)(f(p))是关于(p)的低次多项式、(f(p^c))的值可以快速求出。

    Min_25筛时间复杂度为(O(frac{n^frac{3}{4}}{log n})),空间复杂度为(O(sqrt n)),实现常数较小。

    Min_25筛的大体思想是将1~n之间的数分为三部分:质数、合数和(第三部分你猜啊)1

    Part1.处理质数

    (mathrm{prime}(i))表示返回(i)是否为质数。

    对于每个(lfloorfrac n x floor),我们先预处理出

    [sum_{i=2}^{lfloorfrac n x floor}[mathrm{prime}(i)]f(i) ]

    由于对于质数(p)(f(p))可以被表示为若干个(p^k)的和,所以我们可以把多项式每一项分别求值再相加,所以我们现在只考虑(f(p)=p^k)的情况。注意这里(f)是完全积性函数。

    (mathrm{minp}(i))表示(i)质因数分解后最小质因子,(p_i)代表从小到大第(i)个质数。设

    [g(a,b)=sum_{i=2}^a[mathrm{prime}(i)ormathrm{minp}(i)>p_b]i^k ]

    直观地说,(g(a,b))就是做(b)轮埃氏筛法后(2)~(a)中剩下的所有数的(k)次幂的和。设(|P|)(2)~(sqrt n)内素数集合的大小,则有

    [sum_{i=2}^{lfloorfrac n x floor}[mathrm{prime}(i)]i^k=g(lfloorfrac n x floor,|P|) ]

    从小到大枚举质数,模拟埃氏筛法的过程,从(g(?,b-1))推导(g(?,b)),不难得到

    [g(a,b)=egin{cases}g(a,b-1)&a<p_b^2\g(a,b-1)-p_b^kleft(g(lfloorfrac a {p_b} floor,b-1)-g(p_{b-1},b-1) ight)&age p_b^2end{cases} ]

    什么意思呢??

    • (a<p_b^2),所有(a)以内的合数已经被筛掉了,所以不影响,直接转移即可。

    • (age p_b^2),我们考虑在第(b)轮筛法中筛掉了哪些数。

      每一个(lelfloorfrac a{p_b} floor)的且不含有(le p_b)的质因子的数的(p_b)倍都会被筛掉

      考虑减去(g(lfloorfrac a{p_b} floor,b-1)),但由于这之中包含了(<p_b)质数,我们在加上(g(p_{b-1},b-1))加回来即可。

      由于(p_{b-1}<sqrt n),所以合法的(lfloorfrac n x floor)中一定存在(p_{b-1})

    由于最后我们只需要所有(lfloorfrac n x floor)(g)值来作为答案,并且在递推过程中只会用到这些值 所以只需要记录(O(sqrt n))(lfloor frac n x floor)递推即可

    即对于每个(lfloorfrac n x floor),我们求出了(sum_{i=2}^{lfloorfrac n x floor}[mathrm{prime}(i)]f(i)=sum_kg(lfloorfrac n x floor,|P|))(|P|)代表(sqrt n)以内的素数集合的大小。

    Part2.计算答案

    [S(a,b)=sum_{i=2}^a[mathrm{minp}(i)ge p_b]f(i) ]

    我们分为两部分处理。

    1. 计算(S(a,b))中所有质数的贡献,即(sum_{i=2}^n[mathrm{minp}(i)ge p_bandmathrm{prime}(i)]f(i))

      这部分可通过处理的(g)快速得到:即为(g(a,|P|)-g(p_{b-1},|P|))

    2. 计算(S(a,b))中所有合数的贡献

      我们枚举最小质因子(p_i)和出现次数(t)

      [sum_{i=b}^{|P|}sum_{p_i^{t+1}le a}left(S(lfloorfrac a {p_t^i} floor,i+1)*f(p_i^t)+f(p_i^{t+1}) ight) ]

      注意后面加上的那玩意儿是因为(S)中1不被考虑在内。

      所以:

      [S(a,b)=egin{cases} 0,&a<p_b\ displaystyle g(a,|P|)-g(p_{b-1},|P|)+sum_{i=b}^{|P|}sum_{p_i^{t+1}le a}left(S(lfloorfrac a {p_t^i} floor,i+1)*f(p_i^t)+f(p_i^{t+1}) ight) ,&a>p_bend{cases} ]

      据说这个直接搜就行了,不加记忆化都跑得飞快-_-||

    实现

    我们可以预处理所有的(lfloor frac n x floor​)的值,并从小到大记为(a_1,dots,a_m​)

    对于(1le ile sqrt n),有(a_i=i)

    对于其它的(i)(lfloorfrac n {a_i} floor=m-i+1)

    这样我们可以实现(O(1))从数字到下标的变换

    在处理(g)数组我们直接用(a)数组中的下标存储,空间复杂度即(O(sqrt n))

    转移(g)的时候只需要从大到小枚举(就像01背包那样,这样直接在原数组转移,滚都不用滚了),对于(a< p_b^2)的那部分不用搭理。。。时间复杂度是(O(frac{n^{frac 3 4}}{log n}))的,不会证。

    转移(S)的时候直接枚举,递归就行(雷哥说是(O((n^2)^{n^2}))的复杂度),复杂度也是(O(frac{n^{frac 3 4}}{log n})),也不会证。

    例子

    SPOJ-DIVCNTK

    给定(n,k),设(sigma_0(x))表示(x)的约数个数,求(sum_{i=1}^nsigma_0(i^k)),满足(n,kle 10^{10}),答案ull自然溢出。

    //SPJ DIVCNTK
    #include <cstdio>
    #include <cmath>
    using namespace std;
    
    long long n, sqt, k;
    long long a[2000010], g[2000010];
    int prime[1000010];
    int m, tot;
    
    int getid(long long x) { return x <= sqt ? x : m - n / x + 1; }
    
    unsigned long long chuans(long long n, int b)
    {
    	if (n < prime[b]) return 0;
    	unsigned long long ans = (k + 1) * (g[getid(n)] - g[getid(prime[b - 1])]);
    	for (int i = b; i <= tot && prime[i] * (long long)prime[i] <= n; i++)
    		for (long long x = prime[i], f = k + 1; x * prime[i] <= n; x *= prime[i], f += k)
    			ans += (chuans(n / x, i + 1) + 1) * f + k;
    	return ans;
    }
    
    int main()
    {
    	int t;
    	scanf("%d", &t);
    	while (t --> 0)
    	{
    		scanf("%lld%lld", &n, &k);
    		sqt = sqrt(n);
    		tot = m = 0;
    		for (long long i = 1; i <= n; i = a[m] + 1)
    			a[++m] = n / (n / i), g[m] = a[m] - 1;
    		for (int i = 2; i <= sqt; i++)
    			if (g[i] != g[i - 1])
    			{
    				prime[++tot] = i;
    				long long sqr = i * (long long)i;
    				for (int j = m; a[j] >= sqr; j--)
    					g[j] -= g[getid(a[j] / i)] - g[i - 1];
    			}
    
    		printf("%llu
    ", chuans(n, 1) + 1);
    	}
    	return 0;
    }
    
  • 相关阅读:
    Win32中使用目录浏览器
    xcode 决断设备类型
    一个简单观察者模式的实现 cocos2dx
    在 vector , map , list 间取得平衡
    ChainOfResponsibility 与 Command
    贪吃蛇游戏制作
    Unicode形成的“?”问题
    MFC中手工创建控件
    CString操作指南【转】
    一个简单例子的设计模式分析
  • 原文地址:https://www.cnblogs.com/oier/p/10287574.html
Copyright © 2011-2022 走看看