线性筛积性函数学习笔记

线性筛积性函数学习笔记

以求解下面这个问题为例,对于任给的n,请求出下列式子的结果,1<=n<=1e7

\\\sum_{i=1}\^{n}\\sum_{j=1}\^{n} (-1)\^{d(i\\cdot j)} \\

cpp 复制代码
const int N=1e7+5,MOD=998244353;

int f[N],K[N],vis[N];
vi pm;

/*
	要计算ΣΣ(-1)^(d(i*j))
	d(n)只有在n为完全平方数时才会是奇数
	令n=x*(y*y),x不含任何平方因子(1除外)
	则d(i*j)为奇数当且仅当i=x*(y*y),且j=x*(z*z) |此时d(i*j)=(x*y*z)*(x*y*z),是奇数
	所以令f(n)=x;
	只需求f(i)=f(j)的对数即可,答案就是这个n*n-所有f相同的对数
	进一步转化为求f(x)的所有取值的种数
	
	当x为p^k时,若k&1,则f(x)=p,否则f(x)=1------------------*

	f(x)是积性函数

	怎么利用线性筛求解f(x)

	PS:线性筛是在i=n/pn,pm[j]=pn时筛到n的,其中pn是n的最小质因子

	考虑新增一个K[]数组,K[n]表示n有多少个pn因子,(n有多少个最小质因子,例如K[12]=2)
	进一步的为了方便计算,我们可以把K[n]表示为pn^k,例如K[12]=4,K[18]=9

	如果pn|(n/pn),K[n]=K[n/pn]*pn,否则K[n]=pn

	容易发现这样的K[n]和(n/K[n])一定互质
	但是也有可能出现n=K[n]的情况,这个时候f[n]可以通过*处求解  即f(n)=pn/f(n/K[n])
	若n!=K[n],则f(n)=f(K[n])*f(n/K[n])   
	这样理清之后就有了下面的代码
*/

void init(){	
	vis[1]=1;
	int n=1e7;
	f[1]=1;
	for(int i=2;i<=n;i++){
		if(!vis[i]){
			pm.pb(i);
			K[i]=i;
			f[i]=i;
		}
		for(int p:pm){	//下面的i*p就是n
			if(p*i>n) break;
			vis[p*i]=1;
			if(i%p==0){  // pn | n/pn
				K[i*p]=K[i]*p;
				if(i*p==K[i]*p){//n=pn^k
					f[i*p]=p/f[i];
				}else{
					f[i*p]=f[i*p/K[i*p]]*f[K[i*p]];
				}
				break;
			}else{
				K[i*p]=p;
				f[i*p]=f[p]*f[i];
				//f[i*p]=f[i*p/K[i*p]]*f[K[i*p]];    <--写成这样也是等价的
			}
		}
	}
}

void cal(){//这是一种化简后的形式
	int n=1e7;
	f[1]=1;
	for(int i=2;i<=n;i++){
		if(!f[i]){
			pm.pb(i);
			f[i]=i;
		}
		for(int p:pm){
			if(p*i>n) break;
			if(f[i]%p==0){
				f[i*p]=f[i]/p;
			}else f[i*p]=f[i]*p;
			if(i%p==0) break;
		}
	}
}