杜教筛

解决问题:已知\(f(i)\)是积性函数,求\(\sum\limits_{i=1}^nf(i)\)

\(S(n)=\sum\limits_{i=1}^nf(i)\)

再找一个积性函数\(g()\),求\(f*g\)的前缀和:
\(\sum\limits_{i=1}^{n}(f*g)(i)\)
\(=\sum\limits_{i=1}^{n}\sum\limits_{d|i}f(d)*g(\frac{i}{d})\)
\(=\sum\limits_{d=1}^{n}g(d)\sum\limits_{i=1}^{\frac{n}{d}}f(i)\)
\(=\sum\limits_{d=1}^{n}g(d)*S(\frac{n}{d})\)

考虑\(g(1)S(n)\)
\(g(1)S(n)\)
\(=\sum\limits_{i=1}^{n}g(i)*S(\frac{n}{i})-\sum\limits_{i=2}^{n}g(i)*S(\frac{n}{i})\)
\(=\sum\limits_{i=1}^{n}(f*g)(i)-\sum\limits_{i=2}^{n}g(i)*S(\frac{n}{i})\)

发现只要找到一个合适的积性函数\(g\)满足可以快速求\(f*g\)\(g\)的前缀和,那么就可以结合除法分块递归求解\(\sum\limits_{i=1}^{n}f(i)\)

复杂度为\(O(n^{\frac{3}{4}})\)

优化:
1.线性筛前\(n^{\frac{2}{3}}\)个答案,复杂度为\(O(n^{\frac{2}{3}})\)
2.中途用哈希表记录答案。

例子:
1.求\(\sum\limits_{i=1}^{n}\varphi(i)\)

\((\varphi*1=id)\),令\(f=\varphi,g=1,f*g=id\)

2.求\(\sum\limits_{i=1}^{n}\mu(i)\)

\(\mu*1=\epsilon\),令\(f=\mu,g=1,f*g=\epsilon\)

模板题
code:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=5000010;
int T,n;
int mu[maxn],sum2[maxn],phi[maxn];
ll sum1[maxn];
bool vis[maxn];
vector<int>prime;
unordered_map<int,ll>mp1;
unordered_map<int,int>mp2;
inline void pre_work(int n)
{
	vis[1]=1;mu[1]=phi[1]=1;
	for(int i=2;i<=n;i++)
	{
		if(!vis[i])prime.push_back(i),mu[i]=-1,phi[i]=i-1;
		for(unsigned int j=0;j<prime.size()&&1ll*i*prime[j]<=n;j++)
		{
			vis[i*prime[j]]=1;
			if(i%prime[j]==0)
			{
				phi[i*prime[j]]=phi[i]*prime[j];
				break;
			}
			phi[i*prime[j]]=phi[i]*(prime[j]-1);
			mu[i*prime[j]]=-mu[i];
		}
	}
	for(int i=1;i<=n;i++)sum1[i]=sum1[i-1]+phi[i],sum2[i]=sum2[i-1]+mu[i];
}
inline ll getphi(ll n)
{
	if(n<=5000000)return sum1[n];
	if(mp1.count(n))return mp1[n];
	ll res=1ll*n*(n+1)/2;
	for(ll l=2,r;l<=n;l=r+1)
	{
		r=n/(n/l);
		res-=1ll*(r-l+1)*getphi(n/l);
	}
	return mp1[n]=res;
}
inline int getmu(int n)
{
	if(n<=5000000)return sum2[n];
	if(mp2.count(n))return mp2[n];
	int res=1;
	for(int l=2,r;l<=n;l=r+1)
	{
		r=n/(n/l);
		res-=(r-l+1)*getmu(n/l);
	}
	return mp2[n]=res;
}
int main()
{
	//freopen("test.in","r",stdin);
	//freopen("test.out","w",stdout);
	pre_work(5000000);
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d",&n);
		printf("%lld %d\n",getphi(n),getmu(n));
	}
	return 0;
}
posted @ 2019-11-28 19:22  nofind  阅读(94)  评论(0编辑  收藏  举报