LOJ #6053. 简单的函数

Description

Solution

(Min25) 筛.
要求出 ((1+p_1⊕c_1)*(1+p_2⊕c_2)*....*(1+p_m⊕c_m)) .
我们可以枚举最小质因子 (p) , 那么就要求剩下选的数都不含小于 (p) 的质因子 , 也就是 (p) 作为最小质因子 .
(S(n,k)) 表示当前要求 (sum_{i=2}^{n}f[i]) , 且最小质因子要为 (p[k]) 的贡献 , (p_k) 是第 (k) 个质数.
那么枚举 (i>k) , 并枚举 (p[i]) 的次数 (c) , 然后递归做 (S(frac{n}{p[i]^c},i)) 即可.
要做这个 , 就要快速求出一段的质因子的 (f(i)) 的和.
于是我们预处理出 (s(i)) 表示小于等于 (i) 的质因子的 (f) 之和 .
方法就是枚举所有质数 , 设 (g(n,i)) 表示已经用 (p_1,p_2,p_3,...,p_i) 筛完了 ([2,n]) 后剩余的数的贡献和.
也就是说 (g(n,i)=sum_{j=2}^{n})[(j) 的最小质因子>=(i)或者 (j) 是质数](*f[j]).
不满足上述条件的数已经被减去了 , 所以不需要被重复减去.
(p_i*p_i>n) 时 , (g(n,i)=g(n,i-1)).
否则 (g(n,i)=g(n,i-1)-(g(frac{n}{p_i},i-1)-g(p_i,i-1))*f(p_i)) .
实现有一定小技巧 , 参见代码 .

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+10,mod=1e9+7;
ll n,s[N],c[N],w[N],p[N];int m=0,sqr,cnt=0;
inline int id(ll x){return x<=sqr?x:m-(n/x)+1;}
//每个数在c,w,s数组中的下标,小于sqrt的显然是本身,否则就是后者.
inline int S(ll n,int k){
	if(n<=p[k])return 0;
	int ret=(s[id(n)]-s[p[k]]+mod)%mod;//一段区间的质因子的f之和.
	for(int i=k+1;i<=cnt && n/p[i]>=p[i];i++){//枚举最小质因子
		int c=1;
		for(ll j=n;(j/=p[i])>=p[i];c++)//枚举次数
			ret=(ret+1ll*(p[i]^c)*S(j,i)+(p[i]^(c+1)))%mod;
	}
	return ret;
}
int main(){
  freopen("pp.in","r",stdin);
  freopen("pp.out","w",stdout);
  cin>>n;
  sqr=sqrt(n);
  for(ll i=1,j;i<=n;i=j+1){
	  w[++m]=j=n/(n/i);
	  //由于S(n,k)中的n只会是n的约数,所以只需要预处理所有约数的贡献和就行了.
	  s[m]=(j&1?(j+1)/2*j:j/2*(j+1))-1;c[m]=j-1;
	  //n为10^10,会爆long long,所以先除再乘.
	  //s[i]是小于等于i的质数和,c[i]是小于等于i的质数个数.
  }
  for(int i=2,t;i<=sqr;i++){
	  if(c[i]==c[i-1])continue;//c[i]==c[i-1]说明这个数不是质数
	  p[++cnt]=i;
	  for(int j=m;w[j]/i>=i;j--)
		  s[j]-=(s[t=id(w[j]/i)]-s[i-1])*i,c[j]-=c[t]-c[i-1];
  }
  //由于f(pi)=pi^1,所以除了f(2)=2+1以外,其他的都是f(p)=f(p)-1
  for(int i=2;i<=m;i++)s[i]-=c[i]-2;
  cout<<(S(n,0)+1)%mod;
  return 0;
}

原文地址:https://www.cnblogs.com/Yuzao/p/9269541.html