zoukankan      html  css  js  c++  java
  • 「学习笔记」min25 筛

    貌似看懂板子了,那么来更一更

    min25筛

    求积性函数前缀和,复杂度 (O(frac{n^{0.75}}{log n})),常数比较小

    不支持多点求,同时需要满足 (f(prime)) 是关于 (prime) 的低次多项式(很低次)

    这个低次比如 (varphi (prime)=prime-1),其实这个东西是个能快速求的多项式就行了

    先把 (sum f(i)) 转成质数的和加合数的和,那么这个算法主要是分成两个部分:第一个 ( m{DP}) 和第二个 ( m{DP})

    Part I:DP

    考虑如下一个 ( m{DP}:)

    [ m{g_k(n,j)=sum_{i=1}^n[iin {}{prime}||minp(i)> P_{j}] f_k(i)} ]

    表示 (n) 以内质数或者所有最小质因子大于 ( m{Prime_j}) 的合数的点处多项式次数为 (k) 的部分

    转移就是删掉最小质因子为 (j) 的数,具体如下:

    [g_k(n,j) =egin{cases}g_k(n,j-1)-p_j^k imes(g_k(frac{n}{p_j},j-1)-g_k(p_j-1,j-1))[p_j^2le n]\g_k(n,j-1) [p_j^2>n]end{cases} ]

    最后是删掉质数,乘的系数表示贡献,考虑它是完全积性函数,所以直接除不用管互质

    关注到转移的时候对于 ([1,n]) 的每个数只关注其 (frac n{ m{Prime}}) 的值,那么先 (sqrt n) 处理出来所有的结果

    这个转移同时也只用到了 (sqrt n) 之内的数字,预处理这些通常是可以接受的

    Part II: DP

    接下来设 (S(n,x)) 表示 (x) 最小质因子大于 (x)(n) 以内的所有的 函数值 之和

    转移如下:

    [S(n,x)=g(n)-sum_x+sum_{p_k^cle n,k> x} f(p_k^c) imes (S(frac{n}{p_k^c},k)+[c eq 1]) ]

    含义其实是比较显然的,注意后面的求和又一次使用了积性函数的性质,非常巧妙

    直接递归统计贡献即可,也不需要记忆化

    实现

    先预处理每个质数处的前缀和和每个 (g_k(x,0)),根据具体含义,这些东西都是可以通过次方前缀和来做出来的

    不难发现对于高次自然数幂前缀和,需要伯努利数或者插值,这也就是处理高次的时候会复杂的原因

    处理 (g_k(x,0)) 时只处理每个整除区间右端点的函数前缀和,然后做第一部分的 ( m{DP})(直接对着式子板抄即可)

    对于第二部分中的 (g(n)-sum_p) 这个直接对着多项式做减法即可,剩下的都是裸的实现,无需赘述

    一些细节:

    • 整除分块的数组要开 (2) 倍,原因是 (lfloor frac{n}x floor) 的结果要分成 (le sqrt n)(ge sqrt n) 的两部分

    • 处理 (1) 的贡献通常的办法是都删掉,最后加上一个 (f(1))

    代码是模板题 LOJ6053

    #include<bits/stdc++.h>
    using namespace std;
    namespace yspm{
    	const int N=2e5+10,mod=1e9+7;
    	int fl[N],pri[N],num,tot,bl,n,id1[N],id2[N],g2[N],g1[N],s1[N],val[N]; 
    	inline int get(int x){return x<=bl?id1[x]:id2[n/x];}
    	inline int calc(int x,int y){
    		if(x<=1||pri[y]>x) return 0;
    		int pos=get(x),ans=del(del(g2[pos],g1[pos]),(s1[y-1]+1-y+mod)%mod);
    		if(y==1) ans=add(ans,2); 
    		for(reg int i=y;i<=num&&pri[i]*pri[i]<=x;++i){
    			int prod=pri[i];
    			for(reg int j=1;prod*pri[i]<=x;prod*=pri[i],++j){
    				ans=add(ans,add(mul(calc(x/prod,i+1),pri[i]^j),pri[i]^(j+1)));
    			} 
    		} return ans;
    	}
    	signed main(){
    		n=read(); bl=sqrt(n);  
    		for(reg int i=2;i<=bl;++i){
    			if(!fl[i]) pri[++num]=i,s1[num]=add(s1[num-1],i);
    			for(reg int j=1;j<=num&&pri[j]*i<=bl;++j){
    				fl[i*pri[j]]=1; if(i%pri[j]==0) break;
    			} 
    		}
    		int i2=5e8+4;
    		for(reg int l=1,r;l<=n;l=r+1){
    			r=(n/(n/l)); val[++tot]=n/l; 
    			g1[tot]=del(val[tot]%mod,1); g2[tot]=mul(add(val[tot]%mod,2),mul(del(val[tot]%mod,1),i2));
    			if(n/l<=bl) id1[n/l]=tot; else id2[r]=tot; 
    		} 
    		for(reg int i=1;i<=num;++i){
    			for(reg int j=1;pri[i]*pri[i]<=val[j];++j){
    				int k=get(val[j]/pri[i]); 
    				g1[j]=del(g1[j],del(g1[k],i-1));
    				g2[j]=del(g2[j],mul(pri[i],del(g2[k],s1[i-1])));
    			} 
    		} printf("%lld
    ",add(1,calc(n,1))); 
    		return 0;
    	}
    } signed main(){return yspm::main();}
    

    例题

    LOJ6625

    当头一棒

    这题目告诉我们 (min25) 筛不一定要写第二部分,(那么这题应该放到 (DP) 杂题……)

    考虑合数处的取值是 ( m{minp(i)-2}),那么使用第一部分的 (DP) 能统计出来每个质数作为多少质数的 ( m{minp})

    这种题对于博主这种一根筋的废物还是很有用的!

  • 相关阅读:
    【知识整理】这可能是最好的性能优化教程(一)
    【工作感悟】Android 开发者,如何提升自己的职场竞争力?
    MySql 主从复制
    MyCat 介绍、分片规则、调优的内容收集
    MyCat 安装部署,实现数据库分片存储
    [转]Activemq管理和基本介绍
    [转]ActiveMQ 即时通讯服务 浅析
    Redis 3.0集群 Window搭建方案
    【转】史上最全的“大数据”学习资源整理
    【转】【漫画解读】HDFS存储原理
  • 原文地址:https://www.cnblogs.com/yspm/p/14933738.html
Copyright © 2011-2022 走看看