题目链接(略多):
SPOJ
POJ3904
洛谷SP4191
vjudge
cn.vjudge.net
题目大意:
给出
n
n
n个数
a
1
,
a
2
,
.
.
.
,
a
n
a_1,a_2,...,a_n
a1,a2,...,an,要求从
n
n
n个数中选出
4
4
4个数
a
,
b
,
c
,
d
a,b,c,d
a,b,c,d,要求
g
c
d
(
a
,
b
,
c
,
d
)
=
1.
gcd(a,b,c,d)=1.
gcd(a,b,c,d)=1.
求共有多少种取法。
数据范围:
n
<
=
1
0
4
,
1
<
=
a
i
<
=
1
0
4
n<=10^4,1<=a_i<=10^4
n<=104,1<=ai<=104,每个测试点包含多组数据。
暴力:枚举四个选到的数,然后大力求
g
c
d
.
.
.
gcd...
gcd...
时间复杂度
O
(
n
4
l
o
g
n
)
.
.
.
O(n^4logn)...
O(n4logn)...在
n
=
1
0
4
n=10^4
n=104的情况下肯定要凉qwq
于是我们珂以考虑上网搜题解……
莫比乌斯反演?
于是我学了一下……(这道题)好像也不是很难?qwq
莫比乌斯反演的主要内容就一条:
若
F
(
n
)
=
Σ
d
∣
n
f
(
d
)
\large F(n)=\Large \Sigma\large_{d|n} {f(d)}
F(n)=Σd∣nf(d),则有
f
(
n
)
=
Σ
d
∣
n
μ
(
d
)
F
(
n
d
)
\large f(n)=\Large \Sigma\large_{d|n}\mu(d)F(\frac{n}{d})
f(n)=Σd∣nμ(d)F(dn) ——(1)
但有时候用的是另外一种形式:
若
F
(
n
)
=
Σ
n
∣
d
f
(
d
)
\large F(n)=\Large \Sigma\large_{n|d} {f(d)}
F(n)=Σn∣df(d),则有
f
(
n
)
=
Σ
n
∣
d
μ
(
d
n
)
F
(
d
)
\large f(n)=\Large\Sigma\large_{n|d}\mu(\frac{d}{n})F(d)
f(n)=Σn∣dμ(nd)F(d) ——(2)
如果这里给出详细证明的话会变得很长……所以这里就不证明了……可以看我的博客qwq
但是这两个玄学式子对这道题有什么用呢?
不妨让
f
(
n
)
f(n)
f(n)表示在所有数中选出
4
4
4个,且
4
4
4个数的
g
c
d
gcd
gcd为
n
n
n的选法。
让
F
(
n
)
F(n)
F(n)表示在所有数中选出
4
4
4个,且
4
4
4个数的
g
c
d
gcd
gcd为
n
n
n的倍数的选法。
现在需要求的是
f
(
1
)
f(1)
f(1)。
珂以发现:
F
(
n
)
=
Σ
n
∣
d
f
(
d
)
\large F(n)=\Large \Sigma\large_{n|d} {f(d)}
F(n)=Σn∣df(d)
要注意的是,这里
d
≤
m
a
x
(
a
1
,
a
2
,
.
.
.
,
a
n
)
d\leq max(a_1,a_2,...,a_n)
d≤max(a1,a2,...,an),否则
f
(
d
)
=
0
f(d)=0
f(d)=0(最大公因数不可能大于所有数的最大值)
然后把这个式子往(2)一套,得到
f
(
n
)
=
Σ
n
∣
d
μ
(
d
n
)
F
(
d
)
\large f(n)=\Large\Sigma\large_{n|d}\mu(\frac{d}{n})F(d)
f(n)=Σn∣dμ(nd)F(d)
所以
令
m
a
x
a
=
m
a
x
(
a
1
,
a
2
,
.
.
.
,
a
n
)
maxa=max(a_1,a_2,...,a_n)
maxa=max(a1,a2,...,an)
f
(
1
)
=
Σ
1
∣
d
μ
(
d
1
)
F
(
d
)
=
Σ
d
=
1
m
a
x
a
μ
(
d
)
F
(
d
)
\large f(1)=\Large\Sigma\large_{1|d}\mu(\frac{d}{1})F(d)=\Large\Sigma\large_{d=1}^{maxa}\mu(d)F(d)
f(1)=Σ1∣dμ(1d)F(d)=Σd=1maxaμ(d)F(d)
所以如果知道了所有的
μ
(
i
)
和
F
(
i
)
\mu(i)和F(i)
μ(i)和F(i),就珂以在
O
(
n
)
O(n)
O(n)的时间复杂度内求解qwq。
然后
μ
(
i
)
\mu(i)
μ(i)是珂以线性筛的……方法和筛质数差不多……具体见代码qwq
F
(
i
)
F(i)
F(i)的求法也比较简单,把这些数分解质因数,令
n
u
m
[
i
]
num[i]
num[i]表示有多少个数能整除
i
i
i,在分解质因数时求出所有的
n
u
m
.
num.
num.
显然,
F
(
i
)
=
C
n
u
m
[
i
]
4
F(i)=\Large C_{num[i]}^4
F(i)=Cnum[i]4
所以
F
(
i
)
F(i)
F(i)能在
O
(
n
n
)
O(n\sqrt n)
O(nn)复杂度内筛出来。
然后算出答案就珂以了……qwq
代码
#include<stdio.h>
#include<cstring>
#include<algorithm>
#include<math.h>
#include<vector>
#define re register int
#define rl register ll
#define lowbit(x) x&(-x)
using namespace std;
typedef long long ll;
int read() {
re x=0,f=1;
char ch=getchar();
while(ch<'0' || ch>'9') {
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0' && ch<='9') {
x=10*x+ch-'0';
ch=getchar();
}
return x*f;
}
inline void write(int x) {
if(x>9) write(x/10);
putchar(x%10+'0');
}
namespace I_Love {
const int Size=10005;
int n,tot,a[Size],p[Size],prime[Size];
bool vis[Size];
void getp(int maxn) {
//线性筛莫比乌斯函数
p[1]=1;
for(re i=2; i<=maxn; i++) {
if(!vis[i]) {
prime[++tot]=i;
p[i]=-1;
}
for(re j=1; j<=tot; j++) {
int now=i*prime[j];
if(now>maxn) break;
vis[now]=true;
//如果i能整除prime[j],就break
//可以证明这样i*prime[j+1]也一定会被筛到
if(!(i%prime[j])) break;
p[now]=-p[i];
}
}
}
ll num[Size],F[Size];
void Divide(int x) {
int sx=sqrt(x);
for(re i=1; i<=sx; i++) {
if(!(x%i)) {
num[i]++;
num[x/i]++;
}
}
if(sx*sx==x) {
num[sx]--;
}
}
void Fujibayashi_Ryou() {
getp(10000);
while(scanf("%d",&n)==1) {
int maxn=0;
for(re i=1; i<=n; i++) {
a[i]=read();
if(a[i]>maxn) maxn=a[i];
}
memset(num,0,sizeof(num));
for(re i=1; i<=n; i++) {
//分解质因数,同时求出所有的num
Divide(a[i]);
}
ll ans=0;
for(re i=1; i<=maxn; i++) {
F[i]=num[i]*(num[i]-1)*(num[i]-2)*(num[i]-3)/24;
ans+=F[i]*p[i];
}
printf("%lld\n",ans);
}
}
}
int main() {
I_Love::Fujibayashi_Ryou();
return 0;
}