[SDOI2014]数表
题面:Luogu
题解:推式子
先不管限制条件
\[\begin{split}
ans &= \sum_{i=1}^{n}{\sum_{j=1}^{m}{\sigma(\gcd(i,j))}} \\
&= \sum_{d=1}^{n}{\sigma(d)\sum_{i=1}^{\frac{n}{d}}{\sum_{j=1}^{\frac{m}{d}}{[\gcd(i,j)=1]}}} \\
&= \sum_{d=1}^{n}{\sigma(d)\sum_{x=1}^{\frac{n}{d}}{\mu(x)\frac{n}{xd}\frac{m}{xd}}} \\
&= \sum_{T=1}^{n}{\frac{n}{T}\frac{m}{T}\sum_{d|T}{\sigma(d)\mu(\frac{T}{d})}}
\end{split}
\]
加上限制条件
\[ans=\sum_{T=1}^{n}{\frac{n}{T}\frac{m}{T}\sum_{d|T}{\sigma(d)\mu(\frac{T}{d})}}[\sigma(d)\le a]
\]
于是考虑将询问离线,按\(a\)排序
同时也要将\(\sigma(x)\)离线,可以用树状数组维护更新(单点加,区间和)
除了推式子套了一个数据结构之外,其他的其实还好 毕竟推式子套路多啊
复杂度:\(O(T\sqrt n\log n+n\log^2n)\)
#include<bits/stdc++.h>
using namespace std;
inline void read(int& x)
{
x = 0; char c = getchar();
while (!isdigit(c)) c = getchar();
while (isdigit(c)) x = x * 10 + c - '0', c = getchar();
}
#define maxn 100005
#define N 100000
namespace Bitarray
{
int c[maxn];
inline int lowbit(const int& x) { return x & (-x); }
inline void update(int pos, int val) { for (int i = pos; i <= N; i += lowbit(i)) c[i] += val; }
inline int query(int pos) { int ans = 0; for (int i = pos; i; i -= lowbit(i)) ans += c[i]; return ans; }
};
using namespace Bitarray;
struct Query
{
int n, m, a, id;
bool operator < (const Query& p) const { return a < p.a; }
}q[maxn], f[maxn];
int prime[maxn], vis[maxn], pnum, miu[maxn], ans[maxn], d[maxn];
void eular()
{
miu[1] = 1;
for (int i = 2; i <= N; ++i)
{
if (!vis[i]) prime[++pnum] = i, miu[i] = -1;
for (int j = 1; j <= pnum && i * prime[j] <= N; ++j)
{
vis[i * prime[j]] = 1;
if (i % prime[j]) miu[i * prime[j]] = -miu[i];
else break;
}
}
for (int i = 1; i <= N; ++i)
for (int j = i; j <= N; j += i) d[j] += i;
for (int i = 1; i <= N; ++i) f[i].a = d[i], f[i].id = i;
sort(f + 1, f + N + 1);
}
inline void solve(Query& x)
{
int mn = min(x.n, x.m);
int& Ans = ans[x.id];
for (int l = 1, r; l <= mn; l = r + 1)
{
r = min(x.n / (x.n / l), x.m / (x.m / l));
Ans += (x.n / l) * (x.m / l) * (query(r) - query(l - 1));
}
}
inline void Update(int x)
{
for (int i = 1; i * x <= N; ++i) update(i * x, miu[i] * d[x]);
}
int main()
{
int T; read(T); eular();
for (int i = 1; i <= T; ++i) read(q[i].n), read(q[i].m), read(q[i].a), q[i].id = i;
sort(q + 1, q + T + 1);
for (int i = 1, r = 0; i <= T; ++i)
{
while (f[r + 1].a <= q[i].a && r < N) Update(f[++r].id);
solve(q[i]);
}
for (int i = 1; i <= T; ++i) printf("%d\n", ans[i] & ((1 << 31) - 1));//%2^31就直接自然溢出
return 0;
}
一切伟大的行动和思想,都有一个微不足道的开始。
There is a negligible beginning in all great action and thought.
There is a negligible beginning in all great action and thought.