显然,对于每一个格子
(
i
,
j
)
(i,j)
(i,j),在不管限制的情况下它对答案的贡献为
σ
(
g
c
d
(
i
,
j
)
)
\sigma(gcd(i,j))
σ(gcd(i,j))(
σ
(
i
)
\sigma(i)
σ(i)表示
i
i
i的约数和)。
那么我们不妨考虑每一个
d
=
g
c
d
(
i
,
j
)
d=gcd(i,j)
d=gcd(i,j)对整个答案的贡献,应该是
σ
(
d
)
∑
i
=
1
n
∑
j
=
1
m
[
g
c
d
(
i
,
j
)
=
=
d
]
\sigma(d)\sum_{i=1}^n\sum_{j=1}^m[gcd(i,j)==d]
σ(d)∑i=1n∑j=1m[gcd(i,j)==d]
反演一下就可以得到
∑
k
=
1
n
⌊
n
k
⌋
⌊
m
k
⌋
∑
d
∣
k
μ
(
k
d
)
σ
(
d
)
\sum_{k=1}^n\lfloor\frac{n}{k}\rfloor\lfloor\frac{m}{k}\rfloor\sum_{d|k}\mu(\frac{k}{d})\sigma(d)
∑k=1n⌊kn⌋⌊km⌋∑d∣kμ(dk)σ(d)
然后再重新考虑一下询问中
a
a
a的限制,我们考虑把询问离线下来按照
a
a
a的大小排序,同时也把所有数按照
σ
(
i
)
\sigma(i)
σ(i)排序。
那么我们考虑在把
i
i
i一个一个加进来的时候怎么维护答案。令
f
(
i
)
=
∑
d
∣
i
μ
(
i
d
)
σ
(
d
)
f(i)=\sum_{d|i}\mu(\frac{i}{d})\sigma(d)
f(i)=∑d∣iμ(di)σ(d),那么我们只要维护出
f
(
i
)
f(i)
f(i)的前缀和就可以了。维护的话直接暴力用
O
(
log
2
n
)
O(\log^2n)
O(log2n)的复杂度更新,这个用树状数组实现即可。
时间复杂度:
O
(
q
(
n
log
n
+
log
2
n
)
)
O(q(\sqrt n\log n+\log^2n))
O(q(nlogn+log2n))
因为是对
2
31
2^{31}
231取模,所以直接自然溢出就可以了。
代码
#include<bits/stdc++.h>#define N 100010usingnamespace std;template<typename T>voidchkmax(T &x, T y){x = x > y ? x : y;}template<typename T>voidchkmin(T &x, T y){x = x > y ? y : x;}template<typename T>voidread(T &x){
x =0;int f =1;char c =getchar();while(!isdigit(c)){if(c =='-') f =-1; c =getchar();}while(isdigit(c)) x = x *10+ c -'0', c =getchar(); x *= f;}struct Node {int x, id;booloperator<(const Node &a)const{return x < a.x;}} a[N];struct Que {int n, m, s, id;booloperator<(const Que &b)const{if(s != b.s)return s < b.s;return n < b.n;}} b[N];struct BIT {int f[N];intlowbit(int x){return x &-x;}voidmodify(int x,int v){for(; x <=1e5; x +=lowbit(x)) f[x]+= v;}intquery(int x){int ret =0;for(; x; x -=lowbit(x)) ret += f[x];return ret;}} T;bool f[N];int p[N], s[N], mu[N], ans[N];voidsieve(int n){
mu[1]=1;int len =0;memset(f,true,sizeof(f));for(int i =1; i <= n; i++)for(int j = i; j <= n; j += i)
s[j]+= i;for(int i =2; i <= n; i++){if(f[i]) p[++len]= i, mu[i]=-1;for(int j =1; j <= len && i * p[j]<= n; j++){int k = i * p[j]; f[k]=false;if(i % p[j]==0){mu[k]=0;break;}
mu[k]=-mu[i];}}for(int i =1; i <= n; i++) a[i]=(Node){s[i], i};sort(a +1, a + n +1);}intsolve(int n,int m){int ret =0, x =0;for(int i =1; i <= n; i = x +1){
x =min(n /(n / i), m /(m / i));
ret +=(T.query(x)- T.query(i -1))*(n / i)*(m / i);}return ret;}intmain(){sieve(1e5);int q;read(q);for(int i =1; i <= q; i++){int n, m, s;read(n),read(m),read(s);if(n > m)swap(n, m);
b[i]=(Que){n, m, s, i};}sort(b +1, b + q +1);int sum =0, l =1;for(int i =1; i <= q; i++){while(l <=1e5&& a[l].x <= b[i].s){int x = a[l++].id;for(int j = x; j <=1e5; j += x)
T.modify(j, s[x]* mu[j / x]);}
ans[b[i].id]=solve(b[i].n, b[i].m);if(ans[b[i].id]<0) ans[b[i].id]+=1ll<<31;}for(int i =1; i <= q; i++) cout << ans[i]<<"\n";return0;}