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;
}

posted @ 2018-08-04 19:32  PIPIBoss  阅读(673)  评论(0编辑  收藏  举报