• 杜教筛瞎扯


    原来这东西这么简单的么……qwq


    杜教筛用来快速筛出一系列数论函数的前缀和,并且不要求积性。
    我们设要筛的函数为 (S(n)=sum_{i=1}^{n}f(i)),如果能找到一个函数 (g),考虑如下的过程:

    [sum_{i=1}^n(f*g)(i)=sum_{i=1}^nsum_{dmid i}f(d)gleft(frac{i}{d} ight)=sum_{d=1}^ng(d)sum_{k=1}^{leftlfloorfrac{n}{d} ight floor}f(k)=sum_{d=1}^ng(d)Sleft(leftlfloorfrac{n}{d} ight floor ight) ]

    我们将右边和式的第一项 (g(1)S(n)) 提出来放到左边,得到

    [g(1)S(n)=sum_{i=1}^n(f*g)(i)-sum_{i=2}^ng(i)Sleft(leftlfloorfrac{n}{i} ight floor ight) ]

    可以看到我们已经得到了一个关于 (S(n)) 的递归计算式。如果我们能快速求出 (f*g,g) 的前缀和(通常都是 (O(1)),不过只要计算这俩的复杂度不超过单次杜教筛的复杂度也可以,杜教筛套杜教筛),就可以数论分块来递归计算 (S) 了。
    下面来计算一下复杂度,略去递归的话单次计算显然是 (O(sqrt n)) 的,也就是说如果 (f*g,g) 的复杂度不超过 (O(sqrt n)) 是不影响杜教筛总复杂度的。复杂度的总计算式就是

    [Oleft(sum_{i=1}^{sqrt n}sqrt i+sum_{i=1}^{sqrt n}sqrt{leftlfloorfrac{n}{i} ight floor} ight)=Oleft(sum_{i=1}^{sqrt n}sqrt{leftlfloorfrac{n}{i} ight floor} ight)=Oleft(int_{1}^{sqrt n}sqrt{frac{n}{x}}{ m d}x ight)=Oleft(n^{frac{3}{4}} ight) ]

    但是这样还不够快。注意到我们递归计算的 (n) 是一个这样的数列:

    [1,2,dots,sqrt n,leftlfloorfrac n1 ight floor,leftlfloorfrac n2 ight floor,dots,leftlfloorfrac n{sqrt n} ight floor ]

    如果对于较小的 (n) 我们仍 (O(sqrt n)) 计算显然是非常不明智的。如果能我们预处理出 (S(1sim n^c)) 的值(其中 (c>frac{1}{2})),那么复杂度将变成

    [Oleft(n^c+sum_{i=1}^{n^{1-c}}sqrt{leftlfloorfrac ni ight floor} ight)=Oleft(n^c+int_1^{n^{1-c}}sqrt{frac nx}{ m d}x ight)=Oleft(n^c+n^{1-frac12 c} ight) ]

    (c=frac23) 时达到最小,为 (O(n^{frac23}))
    对于记忆化数组,由于每次递归的参数都是 (leftlfloorfrac nx ight floor) 的形式,且对于 (x>n^{frac13}) 已经预处理过了,所以我们只需开一个 (n^{frac13}) 大小的数组表示 (x) 即可。


    讲完啦!看道模板吧:Luogu4213【模板】杜教筛(Sum)
    题目很简单了,由于 (varphi*1=operatorname{id},mu*1=epsilon),于是 (g,f*g) 就构造出来了。

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    typedef unsigned int uint;
    typedef pair<ll,int> pli;
    
    const int N=pow(INT_MAX,0.666)+1;
    const int M=pow(INT_MAX,0.333)+1;
    int n,pri[N+5];
    ll phi[N+5],mu[N+5];
    bool v[N+5],vis[M+5];
    pli res[M+5];
    
    pli DuSieve(int x)
    {
    	if(x<=N) return make_pair(phi[x],mu[x]);
    	int nx=n/x;
    	if(!vis[nx])
    	{
    		vis[nx]=1;
    		ll v1=x*(1LL*x+1)/2; int v2=1;
    		for(uint l=2,r;l<=x;l=r+1)
    		{
    			r=x/(x/l); pli t=DuSieve(x/l);
    			v1-=(r-l+1)*t.first,v2-=(r-l+1)*t.second;
    		}
    		res[nx]=make_pair(v1,v2);
    	}
    	return res[nx];
    }
    
    int main()
    {
    	mu[1]=phi[1]=1;
    	for(int i=2;i<=N;++i)
    	{
    		if(!v[i]) pri[++pri[0]]=i,mu[i]=-1,phi[i]=i-1;
    		for(int j=1;j<=pri[0]&&i*pri[j]<=N;++j)
    		{
    			v[i*pri[j]]=1;
    			if(i%pri[j]==0) {phi[i*pri[j]]=phi[i]*pri[j]; break;}
    			mu[i*pri[j]]=-mu[i],phi[i*pri[j]]=(pri[j]-1)*phi[i];
    		}
    		phi[i]+=phi[i-1],mu[i]+=mu[i-1];
    	}
    	int Case; scanf("%d",&Case);
    	while(Case--)
    	{
    		scanf("%d",&n); 
    		memset(vis,0,sizeof(vis));
    		pli ans=DuSieve(n);
    		printf("%lld %d
    ",ans.first,ans.second);
    	}
    	return 0;
    }
    

    以后会随缘更几道例题吧(咕咕咕

  • 相关阅读:
    Golang的安装包方法
    Debian kvm网络配置
    Debian-Linux配置网卡网络方法
    KVM虚拟机网络配置 Bridge方式,NAT方式
    WebRTC之框架与接口
    WebRTC
    关于golang.org/x包问题
    http内网转发
    linux服务器可以访问IP访问不了域名地址
    golang--生成某区间的随机数
  • 原文地址:https://www.cnblogs.com/wzzyr24/p/13191372.html
Copyright © 2020-2023  润新知