P5176 学习笔记
手刃一道黑的蒟蒻太高兴了!!!
给定三个整数 \(n,m,p\),求
\[\sum_{i=1}^n\sum_{j=1}^m\sum_{k=1}^p\gcd(ij,ik,jk)\cdot \gcd(i,j,k)\cdot \left(\frac{\gcd(i,j)}{\gcd(i,k)\cdot \gcd(j,k)}+\frac{\gcd(i,k)}{\gcd(i,j)\cdot \gcd(j,k)}+\frac{\gcd(j,k)}{\gcd(i,j)\cdot \gcd(i,k)}\right)
\]
式子好长,看得蒟蒻都畏惧了。
注意到恒等式
\[\gcd(ij,ik,jk)=\frac{\gcd(i,j)\cdot\gcd(k,j)\cdot\gcd(i,k)}{\gcd(i,j,k)}
\]
证明
要证 $$ \gcd(ab,bc,ca)=\frac{\gcd(a,b)\cdot\gcd(b,c)\cdot\gcd(c,a)}{\gcd(a,b,c)} $$ 令 $a,b,c$ 的质因数分解形式分别为 $$ a=\prod_p p^{a_p} $$ $$ b=\prod_p p^{b_p} $$ $$ c=\prod_p p^{c_p} $$ 对于 $\forall p$ 为质数,考虑左式与右式中 $p$ 的指数。显然,左边 \(p\) 的指数为
\[\min(a_p+b_p,b_p+c_p,c_p+a_p)
\]
右边 \(p\) 在分子中的指数为
\[\min(a_p,b_p)+\min(b_p,c_p)+\min(c_p,a_p)
\]
在分母中的指数为
\[\min(a_p,b_p,c_p)
\]
于是 \(p\) 在右式中的指数为
\[\min(a_p,b_p)+\min(b_p,c_p)+\min(c_p,a_p)-\min(a_p,b_p,c_p)
\]
所以要证原命题,只需证
\[\min(a_p+b_p,b_p+c_p,c_p+a_p)=\min(a_p,b_p)+\min(b_p,c_p)+\min(c_p,a_p)-\min(a_p,b_p,c_p)
\]
由于上式关于 \(a_p,b_p,c_p\) 轮换对称,不妨设 \(a_p \le b_p \le c_p\),则左式为
\[LHS=\min(a_p+b_p,b_p+c_p,c_p+a_p)=a_p+b_p
\]
右式为
\[\begin{align*}
RHS&=\min(a_p,b_p)+\min(b_p,c_p)+\min(c_p,a_p)-\min(a_p,b_p,c_p)\\
&=a_p+b_p+a_p-a_p=a_p+b_p\\
\end{align*}
\]
所以左式等于右式,即原命题也成立,证毕。
然后你就会发现这个东西的一部分就是 这个题 中 \(k=2\) 的情况。所以你们知道这个题为什么评黑了吧。
令
\[F(s,t)=\sum_{i=1}^s \sum_{j=1}^t \gcd(i,j)^2
\]
则原式可化简为
\[pF(n,m)+nF(m,p)+mF(n,p)
\]
考虑如何计算 \(F\),我们考虑对其进行莫反,即
\[F(s,t)=\sum_{k=1}^{\min(s,t)} \lfloor \frac sk \rfloor \lfloor \frac tk \rfloor G(k)
\]
其中
\[G(k)=k^2 \prod_{p \mid k} (1-p^{-2})
\]
为积性函数。
线性筛预处理 + 整除分块即可。
code
#include <bits/stdc++.h>
#define pub public:
#define pri private:
#define fri friend:
#define Ofile(s) freopen(s".in", "r", stdin), freopen (s".out", "w", stdout)
#define Cfile(s) fclose(stdin), fclose(stdout)
#define fast ios::sync_with_stdio(false); cin.tie(NULL); cout.tie(NULL);
using namespace std;
using ll = long long;
using ull = unsigned long long;
using lb = long double;
using pii = pair<int, int>;
using pll = pair<ll, ll>;
using pil = pair<int, ll>;
using pli = pair<ll, int>;
constexpr int mod = 1e9 + 7;
constexpr int maxn = 2e7 + 5;
ll n, m, p;
ll t, tot, maxnum;
ll G[maxn];
ll prime[maxn];
bool vis[maxn];
vector <tuple <ll, ll, ll> > query; // 离线
void sieve(ll limit) {
G[1] = 1;
for (ll i = 2; i <= limit; i++){
if (!vis[i])
prime[++tot] = i, G[i] = (i * i - 1) % mod;
for (ll j = 1; j <= tot && i * prime[j] <= limit; j++){
vis[i * prime[j]] = true;
if (i % prime[j])
G[i * prime[j]] = G[i] * G[prime[j]] % mod;
else {
G[i * prime[j]] = G[i] * prime[j] % mod * prime[j] % mod;
}
}
}
for (ll i = 1; i <= limit; i++)
G[i] = (G[i - 1] + G[i]) % mod;
} // 线性筛
ll F(ll s, ll t){
if (s > t)
swap(s, t);
ll res = 0;
for (ll pl = 1, pr; pl <= s; pl = pr + 1){
pr = min(s / (s / pl), t / (t / pl));
ll term1 = (s / pl) * (t / pl) % mod;
ll term2 = (G[pr] - G[pl - 1] + mod) % mod;
res += term1 * term2 % mod, res %= mod;
}
return res;
} // 整除分块求 F(s,t) 的值
int main() {
freopen("std.in", "r", stdin);
freopen("std.out", "w", stdout);
fast;
cin >> t;
while (t--){
cin >> n >> m >> p;
query.push_back({n, m, p});
maxnum = max(max(maxnum, n), max(m, p));
}
sieve(maxnum); // 节约复杂度,就不直接把整个数组全筛了,只筛要用的
for (auto [n, m, p] : query){
cout << (p * F(n, m) % mod + n * F(m, p) % mod + m * F(n, p) % mod) % mod << endl; // 刚刚推的答案计算方法
}
return 0;
}

浙公网安备 33010602011771号