[SDOI2014]数表
题面就懒得放了,给个链接:LOJ
自己的想法:
\[\begin{aligned}
&\sum_{i=1}^n\sum_{j=1}^m\sigma(\gcd(i.j))\\
&=\sum_{i=1}^n\sum_{j=1}^m\sum_{x|(i,j)}x\\
&=\sum_{x=1}^n\sum_{i=1}^{\frac{n}{x}}\sum_{j=1}^\frac{m}{x}
\end{aligned}
\]
然后就gg了,无法处理 \(\le a\) 的限制。
下面是正解(by Wolfycz):
\(\sigma(P) = P +1\)
\(a\perp b,\)
\[\begin{aligned}
\sigma(ab)&=\sum_{x|ab}x\\
&=\sum_{x|a}\sum_{y|b}xy\\
&=\sigma(a)\sigma(b)
\end{aligned}
\]
\(a\)包含\(p\)这个质因子
\[\sigma(pa) = \sigma(a) + \sigma(\frac{a}{low(a)})low(ap)
\]
其中 \(low(x)\) 为 \(x\) 最小质因子的指数次幂,若 \(x = \prod p_i^{c_i},p_1<p_2\cdots\) ,则 \(low(x) = p_1^{c_1}\)
然后就可以欧拉筛了。
把 \(\sigma\) 筛出来后,注意到 \(\sum_{d|T}\sigma(d)\mu(\frac{T}{d})\) 只有 \(\sigma(d)\le a\) 才会有贡献,所以我们把询问按 \(a\) 从小到大排序,然后把 \(\sigma(d) \le a\) 的 \(\sigma(d)\mu(\frac{T}{d})\) 插入树状数组,枚举 \(d\) 的倍数,就可以做到 \(O(n\ln n\log n)\)。
查询就数论分块+树状数组区间求和 \(O(q\sqrt n \log_2n)。\)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <fstream>
#define uint unsigned int
typedef long long LL;
typedef unsigned long long uLL;
#define SZ(x) ((int)x.size())
#define ALL(x) (x).begin(), (x).end()
#define MP(x, y) std::make_pair(x, y)
#define DEBUG(...) fprintf(stderr, __VA_ARGS__)
#define GO cerr << "GO" << endl;
using namespace std;
inline void proc_status()
{
ifstream t("/proc/self/status");
cerr << string(istreambuf_iterator<char>(t), istreambuf_iterator<char>()) << endl;
}
template<class T> inline T read()
{
register T x(0);
register char c;
register int f(1);
while (!isdigit(c = getchar())) if (c == '-') f = -1;
while (x = (x << 1) + (x << 3) + (c xor 48), isdigit(c = getchar()));
return x * f;
}
template<typename T> inline bool chkmin(T &a, T b) { return a > b ? a = b, 1 : 0; }
template<typename T> inline bool chkmax(T &a, T b) { return a < b ? a = b, 1 : 0; }
const int maxN = 1e5;
struct Query
{
uint n, m, id;
uint ans;
unsigned int a;
} q[maxN + 2];
struct BIT
{
unsigned int t[maxN + 2];
void add(int x, unsigned int y) { for (; x <= maxN; x += x & -x) t[x] += y; }
unsigned int query(int x)
{
unsigned int ans = 0;
for (; x; x -= x & -x)
ans += t[x];
return ans;
}
} T;
int Q;
bool vis[maxN + 2];
vector<unsigned int> prime;
pair<unsigned int, unsigned int> sigma[maxN + 2];
unsigned int mu[maxN + 2], low[maxN + 2];
void Input()
{
Q = read<int>();
for (int i = 1; i <= Q; ++i)
q[i].n = read<int>(), q[i].m = read<int>(), q[i].a = read<int>(), q[i].id = i;
}
void Init()
{
sigma[1] = MP(1, 1);
mu[1] = 1;
for (register int i = 2; i <= maxN; ++i)
{
if (!vis[i])
{
mu[i] = -1;
low[i] = i;
sigma[i] = MP(1 + i, i);
prime.push_back(i);
}
for (register int j = 0; j < SZ(prime) && prime[j] * i <= maxN; ++j)
{
int t = prime[j] * i;
vis[t] = 1;
if (i % prime[j] == 0)
{
low[t] = low[i] * prime[j];
mu[t] = 0;
sigma[t] = MP(sigma[i].first + low[t] * sigma[i / low[i]].first, t);
}
else
{
low[t] = prime[j];
mu[t] = -mu[i];
sigma[t] = MP(sigma[i].first * sigma[prime[j]].first, t);
}
}
}
//DEBUG("%d\n", sigma[9].first);
sort(sigma + 1, sigma + 1 + maxN);
}
bool cmp(const Query& A, const Query& B) { return A.a < B.a; }
bool cmp2(const Query& A, const Query& B) { return A.id < B.id; }
void Solve()
{
sort(q + 1, q + 1 + Q, cmp);
int pos = 0;
for (int i = 1; i <= Q; ++i)
{
while (sigma[pos + 1].first <= q[i].a and pos < maxN)
{
pos++;
for (int j = 1; j * sigma[pos].second <= maxN; ++j)
T.add(sigma[pos].second * j, sigma[pos].first * mu[j]);
}
unsigned int ans = 0;
if (q[i].n > q[i].m) swap(q[i].n, q[i].m);
int n = q[i].n, m = q[i].m;
for (register int l = 1, r; l <= n; l = r + 1)
{
r = min(n / (n / l), m / (m / l));
ans += (T.query(r) - T.query(l - 1)) * (n / l) * (m / l);
}
q[i].ans = ans & 0x7FFFFFFF;
}
sort(q + 1, q + 1 + Q, cmp2);
for (int i = 1; i <= Q; ++i)
printf("%d\n", q[i].ans);
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("xhc.in", "r", stdin);
freopen("xhc.out", "w", stdout);
#endif
Input();
Init();
Solve();
return 0;
}