51nod1238 最小公倍数之和V3【杜教筛】

版权声明:本文为博主原创文章,未经博主允许必须转载。 https://blog.csdn.net/C20181220_xiang_m_y/article/details/84894901

题意:

i = 1 n j = 1 n l c m ( i , j )          m o d   1 0 9 ​ + ​ 7 n < = 1 0 10 \sum_{i=1}^n\sum_{j=1}^nlcm(i,j) ~~~~~~~~mod~10^9\!+\!7\\n<=10^{10}

题目分析:

化简, i = 1 n j = 1 n l c m ( i , j ) = i = 1 n j = 1 n i j g c d ( i , j ) \sum_{i=1}^n\sum_{j=1}^nlcm(i,j)\\=\sum_{i=1}^n\sum_{j=1}^n\frac {i*j}{gcd(i,j)}
枚举gcd = d = 1 n d i = 1 n d j = 1 n d [ g c d ( i , j ) = = 1 ] i j =\sum_{d=1}^nd*\sum_{i=1}^{\frac nd}\sum_{j=1}^{\frac nd}[gcd(i,j)==1]*i*j
OK,且慢,这里的gcd(i,j)==1就很讲究了,有两种方法

法一:欧拉函数 φ \varphi

因为 i = 1 n d j = 1 n d \sum_{i=1}^{\frac nd}\sum_{j=1}^{\frac nd} 是对称的,所以可以化为
= d = 1 n d { ( 2 i = 1 n d i j = 1 i j [ ( i , j ) = = 1 ] ) 1 } = d = 1 n d { ( 2 i = 1 n d i i φ ( i ) + [ i = 1 ] 2 ) 1 } = d = 1 n d i = 1 n d i 2 φ ( i ) =\sum_{d=1}^nd*\left\{\left(2\sum_{i=1}^{\frac nd}i\sum_{j=1}^ij[(i,j)==1]\right)-1\right\}\\=\sum_{d=1}^nd*\left\{\left(2\sum_{i=1}^{\frac nd}i*\frac{i*\varphi(i)+[i=1]}2\right)-1\right\}\\=\sum_{d=1}^nd*\sum_{i=1}^{\frac nd}i^2*\varphi(i)
然后按照d分块优化,要求 i 2 φ ( i ) i^2*\varphi(i) 的前缀和,把它看做 i d i d φ id\cdot id\cdot\varphi
那么 ( i d i d φ ) ( i d i d ) = (id\cdot id\cdot\varphi)*(id\cdot id)=
i n i 2 φ ( i ) ( n i ) 2 = n 2 i n φ ( i ) = n 3 = i d 3 \sum_{i|n}i^2*\varphi(i)*(\frac ni)^2\\=n^2\sum_{i|n}\varphi(i)\\=n^3\\=id^3
i d 2 i d 3 id^2和id^3 的前缀和都很好求,所以问题就解决了
i = 1 n i 3 = i = 1 n d i d 2 φ ( d ) ( i d ) 2 = i = 1 n i 2 d = 1 n i d 2 φ ( d ) = i = 1 n i 2 F ( n i ) \sum_{i=1}^ni^3=\sum_{i=1}^n\sum_{d|i}d^2*\varphi(d)*(\frac id)^2\\=\sum_{i=1}^ni^2\sum_{d=1}^{\frac ni}d^2*\varphi(d)\\=\sum_{i=1}^ni^2*F(\frac ni)
把i=1提出来,那么 F ( n ) = i = 1 n i 3 i = 2 n i 2 F ( n i ) F(n)=\sum_{i=1}^ni^3-\sum_{i=2}^ni^2*F(\frac ni)
i 2 φ ( i ) i^2*\varphi(i) 是积性函数,可以线性筛一部分,然后分块优化递归求解即可

法一的核心点: i = 1 n i [ ( n , i ) = = 1 ] = n φ ( n ) + [ n = 1 ] 2 \sum_{i=1}^ni*[(n,i)==1]={n*\varphi(n)+[n=1]\over2}

因为倘若i与n互质,则n-i也与n互质,这样的对数有 φ ( n ) / 2 \varphi(n)/2 对,每对的和都为n

法二:莫比乌斯反演 μ \mu

d = 1 n d i = 1 n d j = 1 n d [ g c d ( i , j ) = = 1 ] i j = d = 1 n d i = 1 n d j = 1 n d i j k ( i , j ) μ ( k ) = d = 1 n d k = 1 n d μ ( k ) k 2 i = 1 n d k j = 1 n d k i j \sum_{d=1}^nd*\sum_{i=1}^{\frac nd}\sum_{j=1}^{\frac nd}[gcd(i,j)==1]*i*j\\=\sum_{d=1}^nd*\sum_{i=1}^{\frac nd}\sum_{j=1}^{\frac nd}i*j*\sum_{k|(i,j)}\mu(k)\\=\sum_{d=1}^nd*\sum_{k=1}^{\frac nd}\mu(k)k^2\sum_{i=1}^{\frac n{dk}}\sum_{j=1}^{\frac n{dk}}i*j
令dk=T
= T = 1 n ( n T ( n T + 1 ) 2 ) 2 k T μ ( k ) k 2 T k \large=\sum_{T=1}^n({\frac nT*(\frac nT+1)\over2})^2*\sum_{k|T}\mu(k)*k^2*{\frac Tk}
按照T分块优化,要求 k T μ ( k ) k 2 T k \sum_{k|T}\mu(k)*k^2*{\frac Tk} 的前缀和(这时候最好不要把T提出去),把它看做 ( i d i d μ ) i d (id\cdot id\cdot\mu)*id
那么 ( i d i d μ ) i d ( i d i d ) = (id\cdot id\cdot\mu)*id*(id\cdot id)=
( i d i d μ ) ( i d i d ) i d = ( d T d 2 μ ( d ) ( T d ) 2 ) i d = ( T 2 d T μ ( d ) ) i d = T N T 2 ( N T ) d T μ ( d ) = N T N T [ T = = 1 ] = i d (id\cdot id\cdot\mu)*(id\cdot id)*id\\=\left(\sum_{d|T}d^2*\mu(d)*(\frac Td)^2\right)*id\\=\left(T^2\sum_{d|T}\mu(d)\right)*id\\=\sum_{T|N}T^2*(\frac NT)\sum_{d|T}\mu(d)\\=N\sum_{T|N}T[T==1]\\=id
i d 2 i d id^2和id 的前缀和都很好求,剩下的部分就和法一的转化方法类似了

法二的核心: [ n = = 1 ] = d n μ ( d ) [n==1]=\sum_{d|n}\mu(d)
两种方法的共同点:

都利用了卷积(递归方法)来求解n比较大时,通常n<=1e9,1e10,1e11,的前缀和问题,构造出 f g = h f*g=h 的形式,当g,h的前缀和可以O(1)求解,且f为积性函数,可以线性筛一部分时,求解f的前缀和的时间复杂度为 O ( n 2 3 ) O(n^{\frac 23})

Ps:两种方法都试图通过卷积消去 φ \varphi μ \mu 前面的系数,从而简化形式,这是个初学时很难想到的点(至少我没想到。。。)。

此处贴出法一的代码,法二请读者自行尝试。。。

#include<cstdio>
#include<map>
#define LL long long
using namespace std;
const int mod = 1e9+7, N = 5000000, inv2 = 5e8+4, inv6 = 166666668;
int p[N/10],sum[N+5];
bool v[N+5];
map<LL,int>F;
inline int sqr(int x){return 1ll*x*x%mod;}
void Prime()
{
	sum[1]=1;int cnt=0;
	for(int i=2;i<=N;i++)
	{
		if(!v[i]) p[++cnt]=i,sum[i]=1ll*i*i%mod*(i-1)%mod;
		for(int j=1,k;j<=cnt&&p[j]*i<=N;j++)
		{
			v[k=p[j]*i]=1;
			if(i%p[j]==0) {sum[k]=1ll*sum[i]*p[j]%mod*sqr(p[j])%mod;break;}
			sum[k]=1ll*sum[i]*sum[p[j]]%mod;
		}
	}
	for(int i=2;i<=N;i++) sum[i]=(sum[i]+sum[i-1])%mod;
}
inline LL calc(LL n){return n*(n+1)%mod*(2*n+1)%mod*inv6%mod;}
int solve(LL n)
{
	if(n<=N) return sum[n];
	if(F.count(n)) return F[n];
	int ret=sqr(n%mod*((n+1)%mod)%mod*inv2%mod);
	for(LL i=2,j;i<=n;i=j+1)
	{
		j=n/(n/i);
		ret=(ret-(calc(j%mod)-calc((i-1)%mod))*solve(n/i))%mod;
	}
	return F[n]=ret;
}
int main()
{
	Prime();
	LL n,ans=0;
	scanf("%lld",&n);
	for(LL i=1,j;i<=n;i=j+1)
	{
		j=n/(n/i);
		ans=(ans+(i+j)%mod*(j-i+1)%mod*inv2%mod*solve(n/i))%mod;
	}
	printf("%lld",(ans+mod)%mod);
}

猜你喜欢

转载自blog.csdn.net/C20181220_xiang_m_y/article/details/84894901