(sum_{i=1}^{n} sum_{j=1}^{m} sum_{d|gcd(i,j)} d)
显然大家都会,跳过了(
然后我们在这里加一个条件。
(sum_{i=1}^{n} sum_{j=1}^{m} sum_{d|gcd(i,j)} d imes [sum_{d|gcd(i,j)} d leq a])
我们暂且忽略这个玩意,定义 (sig_d = sum_{k|d} k)
然后直接枚举这个 (d),贡献是 (sum_{d=1}^{n} sum_{i=1}^{frac{n}{d}} sum_{j=1}^{frac{m}{d}} imes sig_d [gcd(i,j)==1])
反演常用套路,(sum_{d|n} mu_d = [n==1])
枚举一个 (k)
(sum_{d} sig_d sum_{k=1}^{frac{n}{d}} mu_k imes frac{n}{kd} imes frac{m}{kd})
然后我们发现可以令 (T=kd)
换个形式带入
(sum_{T=1}^{n} frac{n}{T} imes frac{m}{T} sum_{d|T} sig_d imes mu_{frac{T}{d}})
然后我们发现只有 (sig_d leq a) 的情况下才计入贡献,然后树状数组扫描线即可。
#include <bits/stdc++.h>
#define int long long
#define pb push_back
#define pii pair<int, int>
using namespace std;
template <class T> inline void cmax(int&x, const T&y) { (x < y) && (x = y); }
template <class T> inline void cmin(int&x, const T&y) { (x > y) && (x = y); }
const int mod = 1ll << 31;
const int MAX = 1e5 + 1;
struct que { int n, m, a, id; };
inline void inc(int&x, int y) {
x += y; if(x >= mod) x -= mod;
}
signed main() {
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
auto getmu = [&](int n) {
vector <int> mu(n, 0); mu[1] = 1;
for(int i = 1; i < n; i++)
for(int j = i * 2; j < n; j += i) mu[j] -= mu[i];
return mu;
};
auto getsig = [&](int n) {
vector <int> sig(n, 0);
for(int i = 1; i < n; i++)
for(int j = i; j < n; j += i) inc(sig[j], i);
return sig;
};
vector <int> mu = getmu(MAX), sig = getsig(MAX), a(MAX);
static int sg[(int)1e5 + 1];
for(int i = 0; i < MAX; i++) sg[i] = sig[i], a[i] = i;
sort(a.begin(), a.end(), [](int x, int y) { return sg[x] < sg[y]; });
int t; cin >> t;
vector <que> q; int cnt = 0;
while(t--) {
int n, m, a;
cin >> n >> m >> a;
if(n > m) swap(n, m);
q.pb({n, m, a, cnt++});
}
sort(q.begin(), q.end(), [](que x, que y) { return x.a < y.a; });
struct BIT {
vector <int> c;
int n;
BIT(const int&_n) { n = _n + 1; c.resize(n + 1); }
inline int low(int x) { return x & -x; }
inline void add(int x, int y) { inc(y, mod); for(; x < n; x += low(x)) inc(c[x], y); }
inline int qry(int x) { int ans = 0; for(; x; x ^= low(x)) inc(ans, c[x]); inc(ans, mod); return ans; }
};
BIT bit(MAX);
auto ins = [&](int x) { for(int k = 1; x * k <= 1e5; k++) bit.add(x * k, mu[k] * sig[x] % mod); };
auto qry = [&](int n, int m) {
int ans = 0, las = 0, now = 0;
for(int l = 1, r = 0; l <= n; l = r + 1) {
r = min(n / (n / l), m / (m / l));
now = bit.qry(r);
inc(ans, (now - las + mod) % mod * (n / l) % mod * (m / l) % mod);
las = now;
}
inc(ans, mod);
return ans;
};
int j = 1;
vector <int> ans(cnt, 0);
for(int i = 0; i < cnt; i++) {
while(j < MAX && sig[a[j]] <= q[i].a) ins(a[j++]);
ans[q[i].id] = qry(q[i].n, q[i].m);
}
for(int i = 0; i < cnt; i++)
cout << ans[i] << '
';
return 0;
}