UVA11424 GCD - Extreme (I)

似乎花了几个月大力学了一波数论
然后发现过了几个月又什么都不会了
所以自己推个式子找下感觉


题意 \large\textbf{题意} 题意

∑ i = 1 N − 1 ∑ j = i + 1 N ( i , j ) \sum\limits_{i=1}^{N-1} \sum\limits_{j=i+1}^N (i,j) i=1N1j=i+1N(i,j)

多组询问,保证答案在long long范围内


思路 \large\textbf{思路} 思路

根据数据范围大概可以判断出这个题需要预处理
以前做过一道求 ∑ i = 1 n ( i , n ) \sum\limits_{i=1}^n(i,n) i=1n(i,n) 的题,发现这道题也可以转化成这个东西
∑ i = 1 N − 1 ∑ j = i + 1 N ( i , j ) = ∑ i = 2 N ( ∑ j = 1 i − 1 ( i , j ) ) \sum\limits_{i=1}^{N-1} \sum\limits_{j=i+1}^N(i,j)=\sum\limits_{i=2}^N \left(\sum\limits_{j=1}^{i-1}(i,j)\right) i=1N1j=i+1N(i,j)=i=2N(j=1i1(i,j))

于是就转化成功了,我们只要能够快速求出后面的式子,就很容易用前缀和解决多组询问的问题

挂个链接吧,这个子问题其实就是这道题
兔队的题解告诉我们子问题可以 Θ ( n ) \Theta(\sqrt n) Θ(n ) 解决,但是这题有多组询问,所以这种方法并不占优势
于是用常规方法, ∑ i = 1 n ( i , n ) = ∑ i ∣ n i × ∑ j = 1 n [ ( j , n ) = = i ] \sum\limits_{i=1}^n(i,n)=\sum_{i\mid n}i\times\sum\limits_{j=1}^n[(j,n)==i] i=1n(i,n)=ini×j=1n[(j,n)==i]
发现 ( j , n ) = i (j,n)=i (j,n)=i 相当于 ( j / i , n / i ) = 1 (j/i,n/i)=1 (j/i,n/i)=1
所以 ∑ j = 1 n [ ( j , n ) = = i ] \sum\limits_{j=1}^n[(j,n)==i] j=1n[(j,n)==i] 相当于与 n / i n/i n/i 互质的数的个数
这时就可以把欧拉函数弄进去了: ∑ i ∣ n i × φ ( n / i ) \sum_{i\mid n}i\times \varphi(n/i) ini×φ(n/i) 就是答案。
我们预处理一下欧拉函数,然后就可以 Θ ( n ) \Theta(\sqrt n) Θ(n ) 算这个式子了
这些操作总的时间复杂度约为 Θ ( n n ) \Theta(n\sqrt n) Θ(nn )

然后 Θ ( n ) \Theta(n) Θ(n) 弄个前缀和, Θ ( 1 ) \Theta(1) Θ(1) 回答每个询问
那么算法的时间复杂度为 Θ ( n n ) \Theta(n\sqrt n) Θ(nn ) ,显然可以通过

不过 Θ ( n n ) \Theta(n\sqrt n) Θ(nn ) 还是太慢了些。事实上有 Θ ( n log ⁡ n ) \Theta(n\log n) Θ(nlogn) 的做法
发现 Θ ( n ) \Theta(\sqrt n) Θ(n ) 枚举因数的过程有很多冗余的判断。这就像我们小学二年级的时候判断质数一样
回想一下,在小学三年级 F l a m i r e \rm F\color{red}lamire Flamire 教了我们埃氏筛法,它通过枚举因子算贡献的方法,使复杂度降至了 Θ ( n log ⁡ n ) \Theta(n \log n) Θ(nlogn)
把埃氏筛法的套路拿过来直接用就行 具体看代码吧


#include<cstdio>
const int N=200000; int n,t,cnt,p[100000];
int v[N+10],phi[N+10]; long long S[N+10];
int main() {
	phi[1]=1;
	for (int i=2; i<=N; ++i) { //线性筛
		v[i]||(p[++cnt]=i,phi[i]=i-1);
		for (int j=1; j<=cnt&&1ll*p[j]*i<=N; ++j) {
			v[t=p[j]*i]=1; phi[t]=phi[i]*(p[j]-1);
			if (!(i%p[j])) { phi[t]+=phi[i]; break; }
		}
	}
	for (int i=1; i<=N; ++i)
		for (int j=i+i; j<=N; j+=i) S[j]+=i*phi[j/i]; //算因子贡献
	for (int i=3; i<=N; ++i) S[i]+=S[i-1]; //前缀和
	while (scanf("%d",&n),n) printf("%lld\n",S[n]); //回答询问
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值