题解 UVA11426 【拿行李(极限版) GCD - Extreme (II)】
原题地址UVA11426 拿行李(极限版) GCD - Extreme (II)
前置芝士:莫比乌斯反演,欧几里得算法
同类题目:UVA11417 GCD
只要你会用for循环,你就能过的题
纯水经验刷题数目的题,此题过后其他均可通过
正文:
你会发现在计算j时重复计算了i的那么我们考虑先将其看作单独的i,j,随后将重复部分减去即可
\(\begin{aligned}\sum_{i = 1}^n\sum_{j = 1}^n\gcd(i, j)= \sum_{d = 1}^nd\sum_{i = 1}^n\sum_{j = 1}^n[\gcd(i, j) = d]\end{aligned}\)
到了这里,我们发现对于\([gcd(i,j)=1]\)我们可以根据莫比乌斯函数的定义\(\begin{aligned}\sum_{d\mid n}\mu(d)\ =[n=1]\end{aligned}\)来进行替换,即\(\begin{aligned}\sum_{d=1}^n\sum_{i=1}^\frac{n}{d}\sum_{j=1}^\frac{n}{d}\sum_{p\mid i,j}\mu(p)\end{aligned}\)
我们可以按照套路将\(p\)提前\(\begin{aligned}\sum_{d=1}^n\sum_{p=1}^\frac{n}{d}\mu(p)\sum_{i=1}^\frac{n}{d}[p\mid i]\sum_{j=1}^\frac{n}{d}[p\mid j]\end{aligned}\)
对于后面的i,j两个和我们进行整除分块来做\(\begin{aligned}\sum_{d = 1}^nd\sum_{p=1}^{\left\lfloor\frac n d\right\rfloor}\mu(p)\left\lfloor\frac n {dp}\right\rfloor\left\lfloor\frac n {dp}\right\rfloor\end{aligned}\)
这里运用一个dp化Q的技巧可以变为\(\begin{aligned}\sum_{Q = 1}^n\sum_{d|Q}d*\mu(\frac Q{d})\left\lfloor\frac n {Q}\right\rfloor^2\end{aligned}\)
然后你会发现我们化简到的\(\begin{aligned}\sum_{d|Q}d*\mu(\frac Q{d})\end{aligned}\)符合\(id*\mu=\varphi\)的形式,所以我们可以进一步简化为\(\begin{aligned}\sum_{Q = 1}^n\varphi(Q)\left\lfloor\frac n {Q}\right\rfloor^2\end{aligned}\),对于询问T次值,总下来复杂度为\(O(n +T\sqrt n)\),可以通过此题,上面那个阉割版的柿子一模一样,照着用代码实现即可,此题最后还要处理一下重复情况
常见卷积
\(σ(n)\):约数和函数,表示n的全部约数和
\(\mu*id_0=\epsilon\)
\(\varphi*id_0=id_1\)
\(\mu*id_1=\varphi\)
\(id_0*id_1=σ\)
\(Code\)
#include<bits/stdc++.h>
#define LL long long
using namespace std;
template <typename T> void read(T & t) {
t = 0;int f = 1;char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-')f =- 1;ch = getchar();}
do{t = t * 10 + ch - '0';ch = getchar();}while(ch >= '0' && ch <= '9');t *= f;
}
const int kato = 4e6 + 1;
bool ispri[kato];
LL n;
LL prime[kato] , phi[kato] , sum[kato] , cnt;
inline void get_phi(){
for(int i = 2;i <= kato;i ++){
if(!ispri[i]){
prime[++ cnt] = i;
phi[i] = i - 1;
}
for(int j = 1;j <= cnt && (i * prime[j] <= kato);j ++){
ispri[i * prime[j]] = 1;
if(i % prime[j] == 0){
phi[i * prime[j]] = phi[i] * prime[j];
break;
}
else{
phi[i * prime[j]] = phi[i] * phi[prime[j]];
}
}
}
for(int i = 1;i <= kato;i ++){
// if(i<=10) cerr<<phi[i]<<"\n";
sum[i] = sum[i - 1] + phi[i];
}
}
inline int Ame_(){
phi[1] = 1 , ispri[1] = 1;
get_phi();
while(cin >> n){
if(!n){
return 0;
}
LL ans = 0;
for(LL l = 1 , r;l <= n;l = r + 1){
r = n / (n / l);
ans += (sum[r] - sum[l - 1]) * (n / l) * (n / l);
}
ans -= n * (n + 1) / 2;
ans /= 2;
printf("%lld\n" , ans);
}
return 0;
}
int Ame__ = Ame_();
signed main(){;}
注意
不知道为什么n不开long long被卡精度卡了半天没过,最后改了才没事,其他的就没什么注意的点了