zoukankan      html  css  js  c++  java
  • Min25筛

    Min25筛

    文章很多的向下取整省略了。。。

    问题引入

    设质数集合为\(p_i\)\(n\)包含的质因数集为\(p(n)\),定义积性函数

    \[F(n)=\left \{ \begin{aligned} 1 && n=1 \\ G(p_i) && n=p_i \\ T(p_i^k) && n=p_i^k,k>1\\ \Pi F(p_i^{c_i})&&n=\Pi p_i^{c_i}\end{aligned} \right. \]

    其中,\(G(p_i)\)为简单多项式函数(复杂了不好搞啊)

    我们要求\(S(n)=\sum _1^n F(i)\)

    \(m=\lfloor \sqrt{n} \rfloor\),我们把答案要求的分成质数和非质数两部分

    接下来的计算过程分为两部分

    Part1.

    对于每一个\(x\in[1,n]\)计算\(\sum _{p_i\leq \frac{n}{x}} G(p_i)\),可以看到这样的\(\frac{n}{x}\)个数是\(O(m)\)

    由于前面提到\(G(p_j)\)是简单的多项式,所以可以拆出\(G(p_j)\)的每一项\(i^k\)分别计算贡献

    考虑对于\(p_j\leq m\)的质数依次筛,定义\(g_k(j,n)\)为考虑前\(j\)个质数,\(\sum _{1<i\leq n,i \in p \or p(i)_{min}>p_j}i^k\)

    即与\(k\in[1,j], p_k\)都互质或者是本身是质数的\(i^k\)之和,注意这个状态是不包括\(1\)

    边界条件\(g_k(0,n)=\sum _2^ni^k\),这个高斯消元/拉格朗日插值法求,咕了

    递推式$\begin{aligned} g_k(j,n)=g_k(j-1,n)-p_j^k (g_k(j-1,\frac{n}{p_j})-g_k(j-1,p_{j-1})) \end{aligned} $

    即每次减去不合法的,把质数的部分取回来

    显然的性质有:

    \(n\leq p_j \rightarrow g_k(j,n)=\sum_{p_i\leq n}p_i^k\)

    $\begin{aligned} n< p_j^2 \rightarrow g_k(j,n)=g_k(j-1,n)-p_j^k (g_k(j-1,\frac{n}{p_j})-g_k(j-1,p_{j-1}))=g_k(j-1,n) \end{aligned} $

    所以\(n< p_j^2\)的部分可以直接跳过(这个剪枝保证了复杂度)

    递推方法:

    由于\(\lfloor \frac{n}{ab}\rfloor=\lfloor \frac{\lfloor\frac{n}{a}\rfloor}{b}\rfloor\)(证明自行意会吧..)

    可以看到每次的\(n'\)都是原先的\(\frac{n}{d}\),这样的数只有\(O(m)\)个,即第二维状态只有\(O(m)\)个,处理出这些状态然后进行递推

    下面是以\(G(p)=p\)为例的递推

    变量名解释:

    \(s_n=\sum_1^n p_i^k ,pri[i]=p_i,st[i]\)存储第\(i\)个状态,\(cnt\)是状态个数

    \(g[n]=g_1(j,n)\),由于递推过程中滚动省去了第一维

    const int N=1e5+10;
    
    int pri[N],notpri[N];
    int id(int x){ return x<=m?x:cnt-n/x+1; } // O(1)访问状态编号
    int st[N],cnt;
    ll g[N],s[N]; 
    
    int main(){
    	rep(i,2,N-1) if(!notpri[i]) {// 预先筛出素数,实际上并不必须
    		pri[++pc]=i;
    		for(int j=i+i;j<N;j+=i) notpri[j]=1;
    	}
        n=rd(),m=sqrt(n);
    	cnt=0; 
    	rep(i,1,n) st[++cnt]=i=n/(n/i),g[cnt]=1ll*i*(i+1)/2-1; // 预处理状态和初始值,不包括1
    	for(sz=1;pri[sz+1]<=m;++sz);
    	rep(i,1,sz) s[i]=s[i-1]+pri[i];
    	for(int i=1;i<=sz;++i) {
    		for(int j=cnt,tmp=pri[i]*pri[i];st[j]>=tmp;--j) {
    			int k=id(st[j]/pri[i]);
    			g[j]-=pri[i]*(g[k]-s[i-1]); // 取回前面的质数
    		}
    	}
    }
    

    \[\ \]

    由于式子比较麻烦,求解复杂度的部分就咕了

    Part2.

    这次要求出答案\(S(n)=\sum F(i)\)

    定义\(f(j,n)=\sum _{p(i)_{min}\ge p_j}F(i)\),求出\(f(1,n)\)后加上\(F(1)\)即可

    \(f(j,n)\)的求解分为两部分

    质数部分:\(\sum _{i\ge j} G(p_i)\),这个可以通过访问先前求出的\(g(j,n)\)来得到

    合数部分:枚举包含的最小质因数为\(p_i(i\ge j,p_i<n)\)

    $\begin{aligned} f(j,n)\leftarrow\sum_{c>1,p_i^c\leq n} F(p_i^c)+\sum F(p_i^c)\cdot f(i+1,\frac{n}{p_i^c}) \end{aligned} $

    剪枝

    1.\(n<p_j\rightarrow f(j,n)=0\)

    2.不需要转移\(p_i^2>n\)的部分

    这一部分由于只有一次查询,所以可以直接用递归来实现

    以下是\(G(p)=p\)的例子

    变量名:都和上面是一样的!

    ll GetF(int j,int n){
    	if(n<pri[j]) return 0;
    	ll ans=g[id(n)]-s[j-1];// 质数的部分,注意不包含前面的j-1个质数
    	for(int i=j;1ll*pri[i]*pri[i]<=n;++i) {
    		int x=pri[i];
    		while(1ll*x*pri[i]<=n){
    			ans+=GetF(i+1,n/x)*x;
    			x*=pri[i];
    			ans+=x;// 转移,两种写在一起了
    		}
    	}
    	return ans;
    }
    
    

    复杂度又咕了

  • 相关阅读:
    ZINTERSTORE — Redis 命令参考
    Wombat vim colorscheme – customized Chronosbox
    我的vim colorscheme 白色之夜 博客园
    Python list of class attributes Python
    vim配色方案colorscheme设置
    colorscheme install
    Python类中的私有成员(私有函数,私有变量)
    Pebble Overview
    用python 发送邮件
    Python发送带附件的Email
  • 原文地址:https://www.cnblogs.com/chasedeath/p/12751225.html
Copyright © 2011-2022 走看看