题面
题解
我们对着屏幕想了好久,终于写出了这个答案的等价式子:
a
n
s
=
∑
x
=
1
n
∑
y
=
1
m
[
(
x
,
y
)
=
1
]
[
(
y
,
k
)
=
1
]
ans=\sum_{x=1}^{n}\sum_{y=1}^{m}[(x,y)=1][(y,k)=1]
ans=x=1∑ny=1∑m[(x,y)=1][(y,k)=1]
然后,我们对着第一个互质进行莫反。
快速莫反的一个方法:记牢这个式子, ∑ i ∣ n μ ( i ) = [ n = 1 ] \sum_{i|n}\mu(i)=[n=1] ∑i∣nμ(i)=[n=1],然后代入原式。
不难得到
a
n
s
=
∑
x
=
1
n
∑
y
=
1
m
[
(
y
,
k
)
=
1
]
∑
d
∣
(
x
,
y
)
μ
(
d
)
=
∑
d
=
1
min
(
n
,
m
)
μ
(
d
)
⋅
⌊
n
d
⌋
⋅
∑
i
=
1
m
/
d
[
(
d
i
,
k
)
=
1
]
=
∑
d
=
1
min
(
n
,
m
)
μ
(
d
)
[
(
d
,
k
)
=
1
]
⋅
⌊
n
d
⌋
⋅
∑
i
=
1
m
/
d
[
(
i
,
k
)
=
1
]
ans=\sum_{x=1}^{n}\sum_{y=1}^{m}[(y,k)=1]\sum_{d|(x,y)}\mu(d)\\ =\sum_{d=1}^{\min(n,m)}\mu(d)\cdot\lfloor\frac{n}{d}\rfloor\cdot\sum_{i=1}^{m/d}[(di,k)=1]\\ =\sum_{d=1}^{\min(n,m)}\mu(d)[(d,k)=1]\cdot\lfloor\frac{n}{d}\rfloor\cdot\sum_{i=1}^{m/d}[(i,k)=1]
ans=x=1∑ny=1∑m[(y,k)=1]d∣(x,y)∑μ(d)=d=1∑min(n,m)μ(d)⋅⌊dn⌋⋅i=1∑m/d[(di,k)=1]=d=1∑min(n,m)μ(d)[(d,k)=1]⋅⌊dn⌋⋅i=1∑m/d[(i,k)=1]
于是我们发现, ⌊ n d ⌋ \lfloor\frac{n}{d}\rfloor ⌊dn⌋ 和 ⌊ m d ⌋ \lfloor\frac{m}{d}\rfloor ⌊dm⌋ 都只有 n , m \sqrt n,\sqrt m n,m 种值,可以数论分块。
那么接下来解决的就是快速求出下面两个函数:
F
(
n
,
k
)
=
∑
i
=
1
n
[
(
i
,
k
)
=
1
]
G
(
n
,
k
)
=
∑
i
=
1
n
μ
(
i
)
[
(
i
,
k
)
=
1
]
F(n,k)=\sum_{i=1}^{n}[(i,k)=1]\\ G(n,k)=\sum_{i=1}^{n}\mu(i)[(i,k)=1]
F(n,k)=i=1∑n[(i,k)=1]G(n,k)=i=1∑nμ(i)[(i,k)=1]
由于这道题通篇出现
k
k
k 的地方也就是判断与另一个数是否互质,所以我们完全可以把
k
k
k 的重复质因子都消去,令
k
=
p
0
p
1
p
2
.
.
.
k=p_0p_1p_2...
k=p0p1p2... ,这样就满足
(
p
,
k
/
p
)
=
1
(p,k/p)=1
(p,k/p)=1 ,方便进行
F
F
F 和
G
G
G 的递归计算:
F
(
n
,
k
)
=
∑
i
=
1
n
[
(
i
,
k
)
=
1
]
=
∑
i
=
1
n
[
(
i
,
k
/
p
0
)
=
1
]
[
(
i
,
p
0
)
=
1
]
=
∑
i
=
1
n
[
(
i
,
k
/
p
0
)
=
1
]
−
∑
i
=
1
n
/
p
0
[
(
i
(
p
0
)
,
k
/
p
0
)
=
0
]
=
F
(
n
,
k
/
p
0
)
−
F
(
n
/
p
0
,
k
/
p
0
)
G
(
n
,
k
)
=
∑
i
=
1
n
μ
(
i
)
[
(
i
,
k
)
=
1
]
=
∑
i
=
1
n
μ
(
i
)
[
(
i
,
k
/
p
0
)
=
1
]
[
(
i
,
p
0
)
=
1
]
=
∑
i
=
1
n
μ
(
i
)
[
(
i
,
k
/
p
0
)
=
1
]
−
∑
i
=
1
n
/
p
0
μ
(
i
p
0
)
[
(
i
,
k
/
p
0
)
=
0
]
=
∑
i
=
1
n
μ
(
i
)
[
(
i
,
k
/
p
0
)
=
1
]
−
μ
(
p
0
)
∑
i
=
1
n
/
p
0
[
(
i
,
p
0
)
=
1
]
μ
(
i
)
[
(
i
,
k
/
p
0
)
=
0
]
=
∑
i
=
1
n
μ
(
i
)
[
(
i
,
k
/
p
0
)
=
1
]
+
∑
i
=
1
n
/
p
0
μ
(
i
)
[
(
i
,
k
)
=
0
]
=
G
(
n
,
k
/
p
0
)
+
G
(
n
/
p
0
,
k
)
F(n,k)=\sum_{i=1}^n [(i,k)=1]=\sum_{i=1}^n [(i,k/p_0)=1][(i,p_0)=1]\\ =\sum_{i=1}^n[(i,k/p_0)=1]-\sum_{i=1}^{n/p_0}[(i(p_0),k/p_0)=0]\\ =F(n,k/p_0)-F(n/p_0,k/p_0)\\ ~\\ ~\\ G(n,k)=\sum_{i=1}^{n}\mu(i)[(i,k)=1]=\sum_{i=1}^{n}\mu(i)[(i,k/p_0)=1][(i,p_0)=1]\\ =\sum_{i=1}^n\mu(i)[(i,k/p_0)=1]-\sum_{i=1}^{n/p_0}\mu(ip_0)[(i,k/p_0)=0]\\ =\sum_{i=1}^n\mu(i)[(i,k/p_0)=1]-\mu(p_0)\sum_{i=1}^{n/p_0}[(i,p_0)=1]\mu(i)[(i,k/p_0)=0]\\ =\sum_{i=1}^n\mu(i)[(i,k/p_0)=1]+\sum_{i=1}^{n/p_0}\mu(i)[(i,k)=0]\\ =G(n,k/p_0)+G(n/p_0,k)
F(n,k)=i=1∑n[(i,k)=1]=i=1∑n[(i,k/p0)=1][(i,p0)=1]=i=1∑n[(i,k/p0)=1]−i=1∑n/p0[(i(p0),k/p0)=0]=F(n,k/p0)−F(n/p0,k/p0) G(n,k)=i=1∑nμ(i)[(i,k)=1]=i=1∑nμ(i)[(i,k/p0)=1][(i,p0)=1]=i=1∑nμ(i)[(i,k/p0)=1]−i=1∑n/p0μ(ip0)[(i,k/p0)=0]=i=1∑nμ(i)[(i,k/p0)=1]−μ(p0)i=1∑n/p0[(i,p0)=1]μ(i)[(i,k/p0)=0]=i=1∑nμ(i)[(i,k/p0)=1]+i=1∑n/p0μ(i)[(i,k)=0]=G(n,k/p0)+G(n/p0,k)
它们的边界都很好想,
F
(
0
,
k
)
=
0
,
F
(
n
,
1
)
=
n
,
G
(
0
,
k
)
=
0
F(0,k)=0,F(n,1)=n,G(0,k)=0
F(0,k)=0,F(n,1)=n,G(0,k)=0 ,但是
G
(
n
,
1
)
=
∑
i
=
1
n
μ
(
i
)
G(n,1)=\sum_{i=1}^n\mu(i)
G(n,1)=i=1∑nμ(i)
我们可以用杜教筛:
(会杜教筛的跳过)
令 S ( n ) = ∑ i = 1 n μ ( i ) S(n)=\sum_{i=1}^n\mu(i) S(n)=∑i=1nμ(i) ,因为
∑ i ∣ x μ ( x / i ) = [ x = 1 ] \sum_{i|x}\mu(x/i)=[x=1] i∣x∑μ(x/i)=[x=1]所以把这个进行求和:
∑ i = 1 n ∑ j ∣ i μ ( i / j ) = ∑ i = 1 n [ i = 1 ] = 1 \sum_{i=1}^{n}\sum_{j|i}\mu(i/j)=\sum_{i=1}^{n}[i=1]=1 i=1∑nj∣i∑μ(i/j)=i=1∑n[i=1]=1简单变一下式:
∑ j = 1 n ∑ j ∣ i n μ ( i / j ) = ∑ j = 1 n ∑ i = 1 n / j μ ( i ) = 1 ∑ i = 1 n μ ( i ) + ∑ j = 2 n ∑ i = 1 n / j μ ( i ) = 1 S ( n ) = 1 − ∑ j = 2 n S ( n / j ) \sum_{j=1}^n\sum_{j|i}^n\mu(i/j)=\sum_{j=1}^{n}\sum_{i=1}^{n/j}\mu(i)=1\\ \sum_{i=1}^n\mu(i)+\sum_{j=2}^n\sum_{i=1}^{n/j}\mu(i)=1\\ S(n)=1-\sum_{j=2}^nS(n/j) j=1∑nj∣i∑nμ(i/j)=j=1∑ni=1∑n/jμ(i)=1i=1∑nμ(i)+j=2∑ni=1∑n/jμ(i)=1S(n)=1−j=2∑nS(n/j)根据这个式子,我们部分记忆化递归、部分预处理,由于数论分块的性质,总的时间复杂度不满 O ( n 2 / 3 ) O(n^{2/3}) O(n2/3) 。
总时间复杂度为 O ( n ⋅ λ ( k ) + n 2 / 3 ) O(\sqrt n\cdot\lambda(k)+n^{2/3}) O(n⋅λ(k)+n2/3) 。
CODE
我在杜教筛的部分,一不小心取模了,后来又没删完,把 “-” 保留成了 “+1000000007-” ,居然能拿 70+,我还以为是个很小的错误。
#include<map>
#include<set>
#include<cmath>
#include<queue>
#include<stack>
#include<random>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define MAXN 100005
#define LL long long
#define ULL unsigned long long
#define ENDL putchar('\n')
#define DB double
#define lowbit(x) (-(x) & (x))
#define FI first
#define SE second
int xchar() {
static const int maxn = 1000000;
static char b[maxn];
static int pos = 0,len = 0;
if(pos == len) pos = 0,len = fread(b,1,maxn,stdin);
if(pos == len) return -1;
return b[pos ++];
}
//#define getchar() xchar()
LL read() {
LL f = 1,x = 0;int s = getchar();
while(s < '0' || s > '9') {if(s<0)return -1;if(s=='-')f=-f;s = getchar();}
while(s >= '0' && s <= '9') {x = (x<<1) + (x<<3) + (s^48);s = getchar();}
return f*x;
}
void putpos(LL x) {if(!x)return ;putpos(x/10);putchar((x%10)^48);}
void putnum(LL x) {
if(!x) {putchar('0');return ;}
if(x<0) putchar('-'),x = -x;
return putpos(x);
}
void AIput(LL x,int c) {putnum(x);putchar(c);}
const int MOD = 1000000007;
int n,m,s,o,k;
int qkpow(int a,int b) {
int res = 1;
while(b > 0) {
if(b & 1) res = res *1ll* a % MOD;
a = a*1ll*a % MOD; b >>= 1;
}return res;
}
const int MXN = 10000000;
int p[MXN+5],mu[MXN+5],cnp;
bool f[MXN+5];
int sieve(int n) {
mu[1] = 1; f[1] = 1;
for(int i = 2;i <= n;i ++) {
if(!f[i]) p[++ cnp] = i,mu[i] = -1;
for(int j = 1;j <= cnp && i*p[j] <= n;j ++) {
f[i*p[j]] = 1;
if(i % p[j] == 0) {mu[i*p[j]]=0;break;}
mu[i*p[j]] = -mu[i];
}
}
for(int i = 1;i <= n;i ++) mu[i] += mu[i-1];
return 0;
}
const int useless = sieve(MXN);
map<int,int> mp;
int S(int n) {
if(n <= 0) return 0;
if(n <= MXN) return mu[n];
if(mp.count(n)) return mp[n];
int ans = 1;
for(int i = 2;i <= n;i ++) {
int r = n/(n/i);
ans -= S(n/i) *1ll* (r-i+1);
i = r;
}
return mp[n] = ans;
}
int pk[2005],cnk;
map<pair<int,int>,LL> mpf;
LL F(int n,int id) {
if(n == 0) return 0;
if(n == 1) return 1;
if(id == 0) return n;
if(mpf.find({n,id}) != mpf.end()) return mpf[{n,id}];
LL ans = F(n,id-1) - F(n/pk[id],id-1);
return mpf[{n,id}] = ans;
}
map<pair<int,int>,LL> mpg;
LL G(int n,int id) {
if(n == 0) return 0;
if(n == 1) return 1;
if(id == 0) return S(n);
if(mpg.find({n,id}) != mpg.end()) return mpg[{n,id}];
LL ans = G(n,id-1) + G(n/pk[id],id);
return mpg[{n,id}] = ans;
}
int main() {
n = read(); m = read(); k = read();
int kk = k;
for(int i = 1;p[i]*p[i] <= kk;i ++) {
if(kk % p[i] == 0) {
pk[++ cnk] = p[i];
while(kk % p[i] == 0) kk /= p[i];
}
}
if(kk > 1) pk[++ cnk] = kk;
LL ans = 0;
for(int i = 1;i <= n && i <= m;i ++) {
int r = min(n/(n/i),m/(m/i));
ans += (G(r,cnk) - G(i-1,cnk)) *1ll* (n/i) *1ll* F(m/i,cnk);
i = r;
}
AIput(ans,'\n');
return 0;
}