CF1717E Madoka and The Best University 题解
\(\text{Preface}\)
思路来自 \(\text{gtm1514}\),所以就不交洛谷题解了,但本着我竟然会推这个玩意的神奇,所以写一发。
这里是 \(\text{gtm1514}\) 大佬的题解,同步发表于洛谷。
\[\sum_{a+b+c = n} { \text{lcm}(c, \gcd(a, b)) }
\]
看到 \(gcd\) 首先给拆出来枚举。
\[\sum_{d=1}^{n} { \sum_{a+b+c = n} { \text{lcm}(c, d)[\gcd(a, b)=d] } }
\]
考虑化简 \([\gcd(a, b)]\)。
根据辗转相除法,有 \(\gcd(x, y) = \gcd(y, x-y)\)
那么代入到 \(\gcd(a, b) = d\) 中即为:
\[\gcd(a, a+b) = d
\]
再将 \(d\) 除过去得到:
\[\gcd(\frac {a} {d}, \frac {a+b} {d}) = 1
\]
由于 \(a+b+c = n\) 所以直接把 \(a+b\) 给换成 \(n-c\)。
\[\gcd(\frac {a} {d}, \frac {n-c} {d}) = 1
\]
即 \(\gcd(\frac {a} {d}, \frac {n-c} {d}) = 1\) 时才对答案有贡献,于是发现这玩意就是个 \(\varphi(\frac {n-c} {d})\)。
此时整个柿子变为:
\[\sum_{d=1}^{n} { \sum_{a+b+c = n} { lcm(c, d) \cdot \varphi(\frac {n-c} {d}) [d | n-c] } }
\]
因为 \(d | n-c\),即 \(n-c\) 是 \(d\) 的倍数,那么就可以枚举这个倍数 \(bs\) 得到 \(n-c = d \cdot bs\),解出来 $ = n - d \cdot bs$,于是就可以算了。
枚举倍数和求 \(\gcd\) 都是 \(O(\log n)\) 的,所以总时间复杂度 \(O(n \log^2 n)\)。
注意这个题 \(\varphi(1) = 0\)。我也不清楚为什么。。
code
#include <iostream>
#define GMY (520&1314)
#define char_phi signed
#define FBI_OPENTHEDOOR(x, y) freopen(#x ".in", "r", stdin), freopen(#y ".out", "w", stdout);
#define re register int
#define DMARK cerr << "###"
#define _ ' '
#define Endl cout << '\n'
#define Dl cerr << '\n'
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
#define N 100005
#define P 1000000007
#define mod %
using namespace std;
inline void Fastio_setup(){ios::sync_with_stdio(false); cin.tie(NULL), cout.tie(NULL);}
/*
我竟然会了
*/
long long n, final_ans;
char nop[N];
long long prime[N], phi[N];
inline void Getphi(){
phi[1] = 0;
for (long long i = 2 ; i <= n ; ++ i){
if (nop[i] == false)
prime[++ prime[0]] = i, phi[i] = i-1;
for (long long j = 1 ; j <= prime[0] ; ++ j){
if (i * prime[j] > n)
break;
nop[i*prime[j]] = true;
if (i % prime[j] == 0)
phi[i*prime[j]] = phi[i] * prime[j];
else
phi[i*prime[j]] = phi[i] * (prime[j]-1);
}
}
}
long long gcd(long long x, long long y) {return ((y == 0) ? (x) : (gcd(y, x%y)));}
inline long long lcm(long long x, long long y) {return (x * y / gcd(x, y));}
inline void work(){
cin >> n;
Getphi();
for (long long d = 1, c ; d <= n ; ++ d){
// 枚举倍数
for (long long bs = 1 ; d*bs <= n ; ++ bs){
c = n - d * bs;
final_ans = (final_ans + lcm(c, d) * phi[bs] mod P) mod P;
}
}
cout << final_ans << '\n';
}
// #define IXINGMY
char_phi main(){
#ifdef IXINGMY
FBI_OPENTHEDOOR(a, a);
#endif
Fastio_setup();
work();
return GMY;
}
再次鸣谢 \(\text{gtm1514}\) 大佬!
$$\huge{\mathcal{Here\ We\ Are,\ Nick\ Of\ Time\ !}}$$