2019.01.19【BZOJ2226】【SPOJ5971】【洛谷P1891】LCMSum(筛法)(数学推理)(欧拉函数)

版权声明:转载请声明出处,谢谢配合。 https://blog.csdn.net/zxyoi_dreamer/article/details/86553725

BZOJ传送门

洛谷传送门

SPOJ传送门


解析:

重温开通博客之前写的一些数学题。

现在还是推了我一段时间的。

思路:

首先我们并不擅长处理 L C M LCM 问题,所以一律换成 g c d gcd

i = 1 n l c m ( i , n ) = i = 1 n i × n g c d ( i , n ) \sum_{i=1}^{n}lcm(i,n)=\sum_{i=1}^n\frac{i\times n}{gcd(i,n)}

接下来需要巧妙的转化:
A n s = i = 1 n i × n g c d ( i , n ) = 1 2 ( i = 1 n 1 i × n g c d ( i , n ) + i = 1 n 1 ( n i ) n g c d ( n i , n ) ) + n \begin{aligned} Ans=&\sum_{i=1}^{n}\frac{i\times n}{gcd(i,n)}\\ =&\frac{1}2(\sum_{i=1}^{n-1}\frac{i\times n}{gcd(i,n)}+\sum_{i=1}^{n-1}\frac{(n-i)*n}{gcd(n-i,n)})+n \end{aligned}

由于 g c d ( i , n ) = g c d ( n i , n ) gcd(i,n)=gcd(n-i,n) ,(不要告诉我你不会 O ( log n ) O(\log n) g c d gcd

所以我们进一步化简: A n s = 1 2 i = 1 n 1 n 2 g c d ( i , n ) + n \begin{aligned} Ans=\frac{1}2\sum_{i=1}^{n-1}\frac{n^2}{gcd(i,n)}+n \end{aligned}

现在我们只需要考虑怎么求这个东西: i = 1 n 1 n 2 g c d ( i , n ) \sum_{i=1}^{n-1}\frac{n^2}{gcd(i,n)}

套路:枚举 g c d gcd
A n s = d n i = 1 n 1 n 2 d [ g c d ( i , n ) = d ] = d n n 2 d i = 1 n 1 [ g c d ( i , n ) = d ] \begin{aligned} Ans=&\sum_{d\mid n}\sum_{i=1}^{n-1}\frac{n^2}{d}[gcd(i,n)=d]\\ =&\sum_{d\mid n}\frac{n^2}d\sum_{i=1}^{n-1}[gcd(i,n)=d] \end{aligned}

。。。
。。。
。。。
这么明显的欧拉函数woc。。。

A n s = d n , d ̸ = n n 2 × ϕ ( n d ) d = n d n , d ̸ = 1 d × ϕ ( d ) \begin{aligned} Ans=&\sum_{d\mid n,d\not=n}\frac{n^2\times \phi(\frac{n}d)}{d}\\ =&n\sum_{d\mid n,d\not=1}d\times \phi(d) \end{aligned}

扫描二维码关注公众号,回复: 4999169 查看本文章

我们只需要筛出欧拉函数,然后枚举倍数加到该加的地方,复杂度是调和级数。相当于 O ( n log n ) O(n\log n)

然后就可以 O ( 1 ) O(1) 回答每个询问了


代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc get_char
#define pc putchar
#define cs const

namespace IO{
	namespace IOONLY{
		cs int Rlen=1<<18|1;
		char buf[Rlen],*p1,*p2;
	}
	inline char get_char(){
		using namespace IOONLY;
		return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
	}
	
	inline int getint(){
		re int num;
		re char c;
		while(!isdigit(c=gc()));num=c^48;
		while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
		return num;
	}
	inline void outint(ll a){
		static char ch[23];
		if(a==0)pc('0');
		while(a)ch[++ch[0]]=a-a/10*10,a/=10;
		while(ch[0])pc(ch[ch[0]--]^48);
	}
}
using namespace IO;

cs int P=1000006;
int phi[P],prime[P],pcnt;
bool mark[P];
ll ans[P];

inline void linear_sieves(int len=P-6){
	phi[1]=1;
	for(int re i=2;i<=len;++i){
		if(!mark[i])prime[++pcnt]=i,phi[i]=i-1;
		for(int re j=1;i*prime[j]<=len;++j){
			mark[i*prime[j]]=true;
			if(i%prime[j]==0){phi[i*prime[j]]=phi[i]*prime[j];break;}
			phi[i*prime[j]]=phi[i]*(prime[j]-1);
		}
	}
	for(int re i=1;i<=len;++i)
	for(int re j=2;j*i<=len;++j)ans[i*j]+=(ll)j*phi[j];
	for(int re i=1;i<=len;++i)ans[i]=ans[i]*i/2+i;
}

signed main(){
	linear_sieves();
	for(int re T=getint();T--;pc('\n'))outint(ans[getint()]);
	return 0;
} 

猜你喜欢

转载自blog.csdn.net/zxyoi_dreamer/article/details/86553725