P3312 [SDOI2014]数表 [莫比乌斯反演,树状数组]
\(\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 \times [\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}} \times 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 \times \frac{n}{kd} \times \frac{m}{kd}\)
然后我们发现可以令 \(T=kd\)
换个形式带入
\(sum_{T=1}^{n} \frac{n}{T} \times \frac{m}{T} \sum_{d|T} sig_d \times 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] << '\n';
return 0;
}