【51nod1227】平均最小公倍数(杜教筛)

传送门


题解:

利用差分,我们要求的实际上是这个玩意:

A n s = ∑ n = 1 N ∑ i = 1 n l c m ( i , n ) n Ans=\sum_{n=1}^{N}\frac{\sum\limits_{i=1}^{n}lcm(i,n)}{n} Ans=n=1Nni=1nlcm(i,n)

根据LCMSUM的推导我们知道:
∑ i = 1 n l c m ( i , n ) = n 2 + n 2 ∑ d ∣ n d ϕ ( d ) \sum_{i=1}^nlcm(i,n)=\frac{n}{2}+\frac{n}{2}\sum_{d\mid n}d\phi(d) i=1nlcm(i,n)=2n+2ndndϕ(d)

所以这里我们知道:
A n s = ∑ n = 1 N ( 1 2 + 1 2 ∑ d ∣ n d ϕ ( d ) ) = N 2 + 1 2 ∑ n = 1 N ∑ d ∣ n d ϕ ( d ) = N 2 + 1 2 ∑ n = 1 N ∑ d = 1 ⌊ N n ⌋ d ϕ ( d ) \begin{aligned} Ans&=&&\sum_{n=1}^N(\frac{1}{2}+\frac{1}{2}\sum_{d\mid n}d\phi (d))\\ &=&&\frac{N}{2}+\frac{1}{2}\sum_{n=1}^N\sum_{d\mid n}d\phi(d)\\ &=&&\frac{N}{2}+\frac{1}{2}\sum_{n=1}^N\sum_{d=1}^{\lfloor\frac{N}{n}\rfloor}d\phi(d) \end{aligned} Ans===n=1N(21+21dndϕ(d))2N+21n=1Ndndϕ(d)2N+21n=1Nd=1nNdϕ(d)

都是很套路的转换。

构造 f = I d ⋅ ϕ , g = I d f=Id\cdot \phi,g=Id f=Idϕ,g=Id,就可以直接杜教筛求出 f f f的前缀和了。


代码:

#include
#define ll long long
#define re register
#define cs const

using std::cerr;
using std::cout;

cs int mod=1e9+7;
inline int add(int a,int b){return (a+=b)>=mod?a-mod:a;}
inline int dec(int a,int b){return (a-=b)<0?a+mod:a;}
inline void Inc(int &a,int b){(a+=b)>=mod&&(a-=mod);}
inline void Dec(int &a,int b){(a-=b)<0&&(a+=mod);}
inline int mul(int a,int b){ll r=(ll)a*b;return r>=mod?r%mod:r;}
cs int inv2=mod+1>>1,inv3=(mod+1)/3,inv6=mul(inv2,inv3);

int l,r;

cs int P=1e6+6,lim=P-6;
int prime[P],pcnt;
bool mark[P];
int f[P];

inline void linear_sieves(){
	f[1]=1;
	for(int re i=2;i<=lim;++i){
		if(!mark[i])prime[++pcnt]=i,f[i]=i-1;
		for(int re j=1;i*prime[j]<=lim;++j){
			mark[i*prime[j]]=true;
			if(i%prime[j]){f[i*prime[j]]=f[i]*(prime[j]-1);}
			else {f[i*prime[j]]=f[i]*prime[j];break;}
		}
	}
	for(int re i=1;i<=lim;++i)f[i]=add(f[i-1],mul(f[i],i));
}

struct Map{
	static cs int magic=1898599;
	int val[magic];
	int key[magic];
	Map(){memset(key,-1,sizeof key);}
	cs int &operator[](cs int &k)cs{
		int h=k%magic;
		while((~key[h])&&(key[h]^k))h=(h+1)%magic;
		return val[h];
	}
	int &operator[](cs int &k){
		int h=k%magic;
		while((~key[h])&&(key[h]^k))h=(h+1)%magic;
		if((key[h]^k)){key[h]=k;}
		return val[h];
	}
	bool find(cs int &k){
		int h=k%magic;
		while((~key[h])&&(key[h]^k))h=(h+1)%magic;
		return key[h]==k;
	}
}sumf;

inline int Sum1(int n){return mul(mul(n,n+1),inv2);}
inline int Sum2(int n){return mul(mul(mul(n,n+1),add(n,n)+1),inv6);}

inline int F(int n){
	if(n<=lim)return f[n];
	if(sumf.find(n))return sumf[n];
	int ans=Sum2(n);
	for(int re i=2,j;i<=n;i=j+1){
		j=n/(n/i);
		Dec(ans,mul(dec(Sum1(j),Sum1(i-1)),F(n/i)));
	}
	return sumf[n]=ans;
}

inline int calc(int n){
	int ans=0;
	for(int re i=1,j;i<=n;i=j+1){
		j=n/(n/i);
		Inc(ans,mul(j-i+1,F(n/i)));
	}
	return mul(n+ans,inv2);
}

signed main(){
	scanf("%d%d",&l,&r);
	linear_sieves();
	cout<<dec(calc(r),calc(l-1));
	return 0;
}

你可能感兴趣的:(筛法)