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

BZOJ传送门

洛谷传送门

SPOJ传送门


解析:

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

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

思路:

首先我们并不擅长处理 L C M LCM LCM问题,所以一律换成 g c d gcd 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)} i=1nlcm(i,n)=i=1ngcd(i,n)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} Ans==i=1ngcd(i,n)i×n21(i=1n1gcd(i,n)i×n+i=1n1gcd(ni,n)(ni)n)+n

由于 g c d ( i , n ) = g c d ( n − i , n ) gcd(i,n)=gcd(n-i,n) gcd(i,n)=gcd(ni,n),(不要告诉我你不会 O ( log ⁡ n ) O(\log n) O(logn) g c d gcd 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} Ans=21i=1n1gcd(i,n)n2+n

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

套路:枚举 g c d gcd 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} Ans==dni=1n1dn2[gcd(i,n)=d]dndn2i=1n1[gcd(i,n)=d]

。。。
。。。
。。。
这么明显的欧拉函数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} Ans==dn,d̸=ndn2×ϕ(dn)ndn,d̸=1d×ϕ(d)

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

然后就可以 O ( 1 ) 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;
} 
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值