文章目录

题目链接:

http://www.51nod.com/Challenge/Problem.html#!#problemId=1237
我们想要求所有的 g c d ( i , j ) gcd(i,j) gcd(i,j)的和,换一个思路就是看 g c d ( i , j ) = d gcd(i,j)=d gcd(i,j)=d对答案的贡献,(感觉好多题都是换一种思路来看对答案的贡献的),那如果能够快速求出 g c d ( i , j ) = d gcd(i,j)=d gcd(i,j)=d的有几个就好了
假如说 g c d ( i , j ) = d gcd(i,j)=d gcd(i,j)=d的有 c n t ( d ) cnt(d) cnt(d)个,那么 a n s = <munderover> d = 1 n </munderover> d c n t ( d ) ans=\sum_{d=1}^nd \cdot cnt(d) ans=d=1ndcnt(d)

那么现在结论就是:
c n t ( d ) = 2 S ( n d ) 1 cnt(d)=2\cdot S(\frac{n}{d})-1 cnt(d)=2S(dn)1
其中S(n)表示欧拉函数前缀和:
S ( n ) = <munderover> i = 1 n </munderover> φ ( i ) S(n)=\sum_{i=1}^n\varphi(i) S(n)=i=1nφ(i)

那么为什么喃?
先来看这个:
<munderover> i = 1 n </munderover> <munderover> j = 1 n </munderover> [ g c d ( i , j ) = = 1 ] = 2 <munderover> i = 1 n </munderover> <munderover> j = 1 i 1 </munderover> [ g c d ( i , j ) = = 1 ] + 1 \sum_{i=1}^n\sum_{j=1}^n[gcd(i,j)==1]=2\sum_{i=1}^n\sum_{j=1}^{i-1}[gcd(i,j)==1]+1 i=1nj=1n[gcd(i,j)==1]=2i=1nj=1i1[gcd(i,j)==1]+1
这个还是很好理解的,因为 g c d ( i , j ) gcd(i,j) gcd(i,j) g c d ( j , i ) gcd(j,i) gcd(j,i)是对称的,主对角线分成两半就行

加1是因为 g c d ( 1 , 1 ) = 1 gcd(1,1)=1 gcd(1,1)=1
然后再修改一哈,把 g c d ( 1 , 1 ) gcd(1,1) gcd(1,1)加进去, j j j 1 1 1 i i i而不是 i 1 i-1 i1
<munderover> i = 1 n </munderover> <munderover> j = 1 n </munderover> [ g c d ( i , j ) = = 1 ] = 2 <munderover> i = 1 n </munderover> <munderover> j = 1 i </munderover> [ g c d ( i , j ) = = 1 ] 1 \sum_{i=1}^n\sum_{j=1}^n[gcd(i,j)==1]=2\sum_{i=1}^n\sum_{j=1}^{i}[gcd(i,j)==1]-1 i=1nj=1n[gcd(i,j)==1]=2i=1nj=1i[gcd(i,j)==1]1
有了这个之后,来看里面那层求和,其实这层求和就是欧拉函数的定义,所以
<munderover> j = 1 i </munderover> [ g c d ( i , j ) = = 1 ] = φ ( i ) \sum_{j=1}^{i}[gcd(i,j)==1]=\varphi(i) j=1i[gcd(i,j)==1]=φ(i)

所以: <munderover> i = 1 n </munderover> <munderover> j = 1 n </munderover> [ g c d ( i , j ) = = 1 ] = 2 <munderover> i = 1 n </munderover> φ ( i ) 1 \sum_{i=1}^n\sum_{j=1}^n[gcd(i,j)==1]=2\sum_{i=1}^n\varphi(i)-1 i=1nj=1n[gcd(i,j)==1]=2i=1nφ(i)1
所以:
<munderover> i = 1 n </munderover> <munderover> j = 1 n </munderover> [ g c d ( i , j ) = = 1 ] = 2 S ( n ) 1 \sum_{i=1}^n\sum_{j=1}^n[gcd(i,j)==1]=2S(n)-1 i=1nj=1n[gcd(i,j)==1]=2S(n)1

那现在要找 g c d ( i , j ) = d gcd(i,j)=d gcd(i,j)=d的个数就是求 i = 1 n j = 1 n [ g c d ( i , j ) = = d ] \sum_{i=1}^n\sum_{j=1}^n[gcd(i,j)==d] i=1nj=1n[gcd(i,j)==d]

因为如果: g c d ( i , j ) = = d gcd(i,j)==d gcd(i,j)==d,那么 g c d ( i d , j d ) = 1 gcd(\frac{i}{d},\frac{j}{d})=1 gcd(di,dj)=1
所以就有:
<munderover> i = 1 n </munderover> <munderover> j = 1 n </munderover> [ g c d ( i , j ) = = d ] = <munderover> i = 1 n d </munderover> <munderover> j = 1 n d </munderover> [ g c d ( i , j ) = = 1 ] = 2 S ( n d ) 1 \sum_{i=1}^n\sum_{j=1}^n[gcd(i,j)==d]=\sum_{i=1}^{\frac{n}{d}}\sum_{j=1}^{\frac{n}{d}}[gcd(i,j)==1]=2\cdot S(\frac{n}{d})-1 i=1nj=1n[gcd(i,j)==d]=i=1dnj=1dn[gcd(i,j)==1]=2S(dn)1

好有了公式 a n s = <munderover> d = 1 n </munderover> d ( 2 S ( n d ) 1 ) ans=\sum_{d=1}^nd \cdot (2\cdot S(\frac{n}{d})-1) ans=d=1nd(2S(dn)1)之后,貌似 d d d还是要从1遍历到n,还是至少都是线性复杂度啊

这里就用到了整除分块了, n d \frac{n}{d} dn不变的这段区间,虽然 d d d会变,但是是公差为1的等差数列,所以这一段的d也能一口气计算,所以整个复杂度就低了

#include"bits/stdc++.h"
using namespace std;
const int maxn=1e6+5;
typedef long long LL;
const LL MOD=1e9+7;
const LL inv2=5e8+4;
bool vis[maxn];
LL phi[maxn],Sphi[maxn];//欧拉函数,欧拉函数前缀和 
vector<int>prime;
map<LL,LL>Mp;
void PHI(int n)
{
	memset(vis,1,sizeof(vis));
	phi[1]=1;
	Sphi[1]=1;
	for(int i=2;i<=n;i++)
	{
		if(vis[i])
		{
			prime.push_back(i);
			phi[i]=i-1;
		}
		for(int j=0;j<prime.size()&&i*prime[j]<=n;j++)
		{
			vis[i*prime[j]]=0;
			if(i%prime[j]==0)
			{
				phi[i*prime[j]]=phi[i]*prime[j];
				break;
			}
			else phi[i*prime[j]]=phi[i]*(prime[j]-1);
		}
		Sphi[i]=(Sphi[i-1]+phi[i])%MOD;
	}
}
LL S(LL n)//计算欧拉函数前缀和 
{
	if(n<=maxn-5)return Sphi[n];
	if(Mp[n])return Mp[n];
	LL res=(n%MOD)*((n+1)%MOD)%MOD*inv2%MOD;
	for(LL i=2,j;i<=n;i=j+1)
	{
		j=n/(n/i);
		res-=(LL)(j-i+1)%MOD*S(n/i)%MOD;
		res%=MOD;
	}
	res=(res+MOD)%MOD;
	return Mp[n]=res;
}
LL calc(LL i,LL j)//计算从i到j的公差为1的等差数列的和 
{
	LL res=(i+j)%MOD*(j-i+1LL)%MOD*inv2%MOD;
	return res%MOD;
 } 
int main()
{
	PHI(maxn-5);
	LL N;
	while(cin>>N)
	{
		LL res=0;
		for(LL d=1,j;d<=N;d=j+1)
		{
			j=N/(N/d);
			res+=calc(d,j)*(2LL*S(N/d)-1)%MOD;
			res%=MOD;
		}
		res=(res+MOD)%MOD;
		cout<<res<<endl;
	}
}