51nod 1238 最小公倍数之和 V3 题解

博客观赏效果更佳

题意简述

∑ i = 1 n ∑ j = 1 n lcm ⁡ ( i , j ) \sum\limits_{i=1}^{n}\sum\limits_{j=1}^{n} \operatorname{lcm}(i,j) i=1nj=1nlcm(i,j)

n ≤ 1 0 10 n\le 10^{10} n1010 (简短题意,恶臭规模)

思路

总的来说,就是先把式子变一变,然后巧妙的用欧拉函数 φ \varphi φ 加上杜教筛和整除分块来算。

变形

首先我们知道 lcm ⁡ ( a , b ) = lcm ⁡ ( b , a ) \operatorname{lcm}(a,b)=\operatorname{lcm}(b,a) lcm(a,b)=lcm(b,a)

所以,当 i ≠ j i\not=j i=j 时候,我们计算 j < i j<i j<i 的情况,然后 × 2 \times 2 ×2 即可

对于 i = j i=j i=j 的情况特判,这时候产生的贡献和为 n ( n + 1 ) 2 \dfrac{n(n+1)}{2} 2n(n+1)

所以,答案化为

∑ i = 1 n ∑ j = 1 i − 1 lcm ⁡ ( i , j ) + n ( n + 1 ) 2 \sum\limits_{i=1}^{n}\sum\limits_{j=1}^{i-1} \operatorname{lcm}(i,j) + \dfrac{n(n+1)}{2} i=1nj=1i1lcm(i,j)+2n(n+1)

我们都知道 lcm ⁡ ( i , j ) = i j gcd ⁡ ( i , j ) \operatorname{lcm}(i,j)=\dfrac{ij}{\gcd(i,j)} lcm(i,j)=gcd(i,j)ij。这个再换一下即可。

开始硬♂上

开局一公式,结论全靠推。让我们开心的推倒式子吧~

首先枚举 g c d gcd gcd,然后枚举互质的倍数 i , j i,j i,j

原式

= ∑ d = 1 n ∑ i = 1 n / d ∑ j = 1 i [ gcd ⁡ ( i , j ) = 1 ] i j d =\sum\limits_{d=1}^{n}\sum\limits_{i=1}^{n/d}\sum\limits_{j=1}^{i} [\gcd(i,j)=1] ijd =d=1ni=1n/dj=1i[gcd(i,j)=1]ijd
= ∑ i = 1 n ∑ j = 1 i [ gcd ⁡ ( i , j ) = 1 ] i j ∑ d = 1 n / i d =\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{i} [\gcd(i,j)=1] ij \sum\limits_{d=1}^{n/i} d =i=1nj=1i[gcd(i,j)=1]ijd=1n/id (枚举一对互质的 i , j i,j i,j ,计算有多少个 d d d 算到了这对 i , j i,j i,j,加起来)
S k ( n ) = ∑ i = 1 n i k S_k(n)=\sum\limits_{i=1}^{n} i^k Sk(n)=i=1nik 。显然, k = 1 , 2 , 3 k=1,2,3 k=1,2,3 时这个式子都能 O ( 1 ) O(1) O(1) 算。
接下来变成
= ∑ i = 1 n ∑ j = 1 i [ gcd ⁡ ( i , j ) = 1 ] i j S 1 ( n / i ) =\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{i}[\gcd(i,j)=1] ij S_1(n/i) =i=1nj=1i[gcd(i,j)=1]ijS1(n/i) (恭喜你少了一个 d d d
考虑如何求 ∑ j = 1 i [ gcd ⁡ ( i , j ) = 1 ] j \sum\limits_{j=1}^{i} [\gcd(i,j)=1]j j=1i[gcd(i,j)=1]j,也就是求 i i i 以内和 i i i 互质的数的和。

{% fold %}

如何求 i 以内和 i 互质的数的和(会的跳过)

首先数量一共有 φ ( i ) \varphi(i) φ(i) 个。
然后我们打表发现,这东西和等差数列有一个很类似的性质:我们把 [ 1 , i ] [1,i] [1,i] 中和 i i i 互质的 j j j 从小到大排一排,换一行,再从大到小排一排,对应位置和相等(都是 i i i)!
根据高斯当年想出来的办法,和就是(每个对应位置的和)乘以(一共有多少项),然后除二。
换到这题来,这个和就是 1 2 i × φ ( i ) \dfrac{1}{2}i\times \varphi(i) 21i×φ(i)
{% endfold %}

它等于 1 2 i × φ ( i ) \dfrac{1}{2}i\times \varphi(i) 21i×φ(i)。带入原式:

= ∑ i = 1 n i × S 1 ( n / i ) × i × φ ( i ) 2 =\sum\limits_{i=1}^{n} i\times S_1(n/i) \times \dfrac{i\times \varphi(i)}{2} =i=1ni×S1(n/i)×2i×φ(i) (恭喜你又少了一个 j j j,它现在是一维了)
= 1 2 ∑ i = 1 n i 2 φ ( i ) × S 1 ( n / i ) =\dfrac{1}{2}\sum\limits_{i=1}^{n} i^2\varphi(i)\times S_1(n/i) =21i=1ni2φ(i)×S1(n/i)

S 1 ( n / i ) S_1(n/i) S1(n/i) 显然可以整除分块,但是整除分块我们要考虑一个东西: 如何求 i 2 φ ( i ) i^2\varphi(i) i2φ(i) 的前缀和?显然,用杜教筛求一下和即可。
{% fold %}

具体怎么杜教筛 (老样子,会的跳过)

f ( n ) = n 2 φ ( n ) f(n)=n^2\varphi(n) f(n)=n2φ(n)
关键就是要配一个好算的 g g g ,使得 f × g f\times g f×g 也是一个好算的函数 h h h

由狄利克雷卷积的定义,
h ( n ) = ∑ d ∣ n f ( d ) g ( n d ) h(n)=\sum\limits_{d|n} f(d)g(\dfrac{n}{d}) h(n)=dnf(d)g(dn)
= ∑ d ∣ n d 2 φ ( i ) g ( n d ) =\sum\limits_{d|n} d^2\varphi(i)g(\dfrac{n}{d}) =dnd2φ(i)g(dn)
我们发现,令 g ( n ) = n 2 g(n)=n^2 g(n)=n2,就能消掉一个 d 2 d^2 d2,原式继续变成:
= ∑ d ∣ n n 2 φ ( i ) =\sum\limits_{d|n} n^2\varphi(i) =dnn2φ(i) (注意 g ( n d ) g(\dfrac{n}{d}) g(dn) 头顶上还有一个 n n n,乘出来变成 n 2 n^2 n2
= n 2 ∑ d ∣ n v a r p h i ( d ) = n 3 n^2\sum\limits_{d|n} varphi(d)=n^3 n2dnvarphi(d)=n3

所以说, h ( n ) = f × ( g ( n ) = n 2 ) = ( n 3 ) h(n)=f\times (g(n)=n^2)=(n^3) h(n)=f×(g(n)=n2)=(n3)

然后用杜教筛的传统艺能就行了。
{% endfold}

总复杂度非常恐怖,但是的确能通过 n = 1 0 1 0 n=10^10 n=1010 的数据。一定注意处处取膜

代码

{% fold %}

#include <bits/stdc++.h>
using namespace std;
namespace Flandre_Scarlet
{
	#define N   20000007ll
	#define mod 1000000007ll
	#define i6  166666668ll
	#define i2  500000004ll
	#define int long long
	#define F(i,l,r) for(int i=l;i<=r;++i)
	#define D(i,r,l) for(int i=r;i>=l;--i)
	#define Fs(i,l,r,c) for(int i=l;i<=r;c)
	#define Ds(i,r,l,c) for(int i=r;i>=l;c)
	#define MEM(x,a) memset(x,a,sizeof(x))
	#define FK(x) MEM(x,0)
	#define Tra(i,u) for(int i=G.Start(u),v=G.To(i);~i;i=G.Next(i),v=G.To(i))
	#define p_b push_back
	#define sz(a) ((int)a.size())
	#define iter(a,p) (a.begin()+p)
	int I()
	{
	    int x=0;char c=getchar();int f=1;
	    while(c<'0' or c>'9') f=(c=='-')?-1:1,c=getchar();
	    while(c>='0' and c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
	    return (x=(f==1)?x:-x);
	}
	void Rd(int cnt,...)
	{
	    va_list args; va_start(args,cnt);
	    F(i,1,cnt) {int* x=va_arg(args,int*);(*x)=I();}
	    va_end(args);
	}
	bitset<N> notp; int primes[N/5];
	int phi[N];
	void Init()
	{
		int n=2e7;
		notp[1]=1; phi[1]=1;
		int &cnt=primes[0];
		F(i,2,n)
		{
			if (!notp[i]) {primes[++cnt]=i; phi[i]=i-1;}
			for(int j=1;j<=cnt and i*primes[j]<=n;++j)
			{
				int u=primes[j];
				notp[i*u]=1;
				if (i%u==0){phi[i*u]=phi[i]*u; break;}
				else phi[i*u]=phi[i]*phi[u];
			}
		}
		F(i,1,n) phi[i]=(phi[i-1]+1ll*phi[i]*i%mod*i%mod)%mod;
	}

	int n;
	void Input()
	{
		n=I();
	}

	map<int,int> rec; 
	int S1(int n) {n%=mod; return 1ll*n%mod*(n+1)%mod*i2%mod;}
	int S2(int n) {n%=mod; return 1ll*n%mod*(n+1)%mod*(2ll*n+1)%mod*i6%mod;}
	int S3(int n) {n%=mod; return 1ll*S1(n)*S1(n)%mod;} // 我就是这里没取膜,调了两小时
	int f(int n) //杜教筛求 f(n)=n^2*phi(n) 的前缀和
	{
		if (n<=2e7) return phi[n];
		if (rec[n]) return rec[n];
		int ans=0;
		for(int l=2,r;l<=n;l=r+1)
		{
			r=n/(n/l);
			ans=(ans+f(n/l)*(S2(r)-S2(l-1)))%mod;
		} 
		n%=mod;
		return rec[n]=(S1(n)*S1(n)-ans)%mod;
	}
	void Soviet()
	{
		int ans=0;
		for(int l=2,r;l<=n;l=r+1)
		{
			r=n/(n/l);
			ans=(ans+S1(n/l)*(f(r)-f(l-1))%mod*i2%mod)%mod;
		}// 整除分块+杜教筛
		printf("%lld\n",((2ll*ans+S1(n))%mod+mod)%mod);
	}

	#define Flan void
	Flan IsMyWife()
	{
		// freopen("ans.txt","w",stdout);
		Init();
		Input();
		Soviet();
	}
	#undef int //long long
}
int main()
{
	Flandre_Scarlet::IsMyWife();
	getchar();getchar();
	return 0;
}

{% endfold %}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值