P3312 [SDOI2014]数表 题解
推式子的时候默认 \(n < m\)
令 \(\sigma(i)\) 表示 \(i\) 的约数和
\[ ans= \sum\limits _{i=1}^n\sum\limits_{j=1}^{n} \sigma(\gcd(i,j))\\
=\sum\limits_{d=1}^{n} \sum \limits_{i=1}^{\frac{n}{d}} \sum \limits_{j=1}^{\frac{m}{d}} \sigma(d)[\gcd(i,j)==1]\\
=\sum\limits_{d=1}^{n} \sum \limits_{i=1}^{\frac{n}{d}} \sum \limits_{j=1}^{\frac{m}{d}} \sum\limits_{x|d}\mu(x) \sigma(d)\\
=\sum\limits_{d=1}^{n} \sigma(d) \sum \limits_{i=1}^{\frac{n}{d}} \sum \limits_{j=1}^{\frac{m}{d}} \sum\limits_{x|d}\mu(x)\\
=\sum\limits_{d=1}^{n} \sigma(d) \sum\limits_{x=1}^{\frac{n}{d}}\mu(x) \dfrac{n}{dx} \dfrac{m}{dx}\\
\sum\limits_{d=1}^n \sigma(d) \sum\limits_{T=1}^{n} \mu(\dfrac{T}{d})\dfrac{n}{T} \dfrac{m}{T}(T=dx)\\
=\sum\limits_{T=1}^{n}\dfrac{n}{T} \dfrac{m}{T}\sum\limits_{d|T}\sigma(d)\mu(\dfrac{T}{d})\\
\]
先线性筛出 \(\mu\) ,\(\sigma\) 可以 \(O(n\log n)\) 暴力算,前面套个整除分块即可。
但是加上 \(a\) 的限制之后。。。
发现只有 \(\sigma(i)\le a\) 的时候才可以对于那个 \(T\) 产生贡献。
所以考虑离线询问,将 \(a\) 从小到大排序,还要预处理所有的 \(\sigma(i)\) 从小到大排序,用树状数组暴力修改即可。
附赠一组较强样数据供调试
Input
1
25291 26958 42646
Output
235628669
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef double db;
#define pb(x) push_back(x)
#define mkp(x,y) make_pair(x,y)
//#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
//char buf[1<<21],*p1=buf,*p2=buf;
#define int long long
inline unsigned read() {
unsigned x=0,f=1;char ch=getchar();
while(!isdigit(ch)) {if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch))x=x*10+(ch^48),ch=getchar();
return x*f;
}
const int N = 20005;
const int M = 100005;
const int mod = 1ll << 31;
int ans[N], tr[M];
bool vis[M];
int mu[M], pri[M], cnt, q, it = 1;
struct node {
int n, m, a, id;
inline bool operator<(const node&rhs)const{return a < rhs.a;}
}a[N];
struct fact {
int id, x;
inline bool operator<(const fact&rhs)const{return x < rhs.x;}
}f[M];
void add(int x, int d) {
for (int i = x; i < M; i += i & -i) tr[i] += d;
}
int ask(int x) {
int res = 0;
for (int i = x; i > 0; i -= i & -i) res += tr[i];
return res;
}
void Sieve() {
mu[1] = 1;
for (int i = 2; i < M; ++ i) {
if (! vis[i]) pri[++ cnt] = i, mu[i] = -1;
for (int j = 1; i * pri[j] < M; ++ j) {
vis[i * pri[j]] = 1;
if(i % pri[j] == 0) break;
mu[i * pri[j]] = - mu[i];
}
}
for (int i = 1; i < M; ++ i)
for (int j = 1; i * j < M; ++ j)
f[i * j].x += i;
for(int i = 1; i < M; ++ i) f[i].id = i;
sort(f + 1, f + M);
}
signed main() {
Sieve();
q = read();
for (int i = 1; i <= q; ++ i)
a[i].n = read(), a[i].m = read(), a[i].a = read(), a[i].id = i;
sort(a + 1, a + q + 1);
for (int i = 1; i <= q; ++ i) {
while (it < M && f[it].x <= a[i].a) {
int t = f[it].id;
for (int j = 1; j * t < M; ++ j) add(j * t, f[it].x * mu[j]);
++ it;
}
int res = 0, n = a[i].n, m = a[i].m;
if (n > m) swap(n, m);
for (int l = 1, r; l <= n; l = r + 1)
r = min(n / (n / l), m / (m / l)), res += (n / l) * (m / l) * (ask(r) - ask(l - 1));
ans[a[i].id] = res;
}
for (int i = 1; i <= q; ++ i) printf("%lld\n", ans[i] % mod);
return 0;
}
路漫漫其修远兮,吾将上下而求索