题意
给出一个长为N的数列a,求
∑
∑
(
a
i
,
a
j
)
∗
(
i
,
j
)
\sum\sum(a_i,a_j)*(i,j)
∑∑(ai,aj)∗(i,j)
1<=i<=N,1<=j<=N
bzoj
loj
题解
看到gcd,一半都要想到莫比乌斯反演
先套路地尝试搞掉一个
原
式
=
∑
d
d
∑
i
∑
j
[
i
,
j
]
=
d
∗
(
a
i
,
a
j
)
原式=\sum_d{d}\sum_i\sum_j{[i,j]=d}*(a_i,a_j)
原式=d∑di∑j∑[i,j]=d∗(ai,aj)
=
∑
d
d
∑
d
′
n
d
m
u
(
d
′
)
∑
i
∑
j
(
a
i
d
d
′
,
a
j
d
d
′
)
=\sum_{d}{d}\sum_{d'}^{\frac{n}{d}}{mu(d')}\sum_i\sum_j(a_{idd'},a_{jdd'})
=d∑dd′∑dnmu(d′)i∑j∑(aidd′,ajdd′)
似乎没什么好化的了
观察后面哪一个东西,等价于一个集合里面,所有数两两的
g
c
d
gcd
gcd和
那么对于每一次,我们把这个集合拿出来,设为
S
S
S
定义
f
(
S
)
f(S)
f(S)就是这个两两的
g
c
d
gcd
gcd和
em…
一个比较显然的做法是直接套上莫比乌斯反演
也就是先得到
F
(
i
)
F(i)
F(i)表示gcd是i的倍数的答案
然后再用莫比乌斯返回去
但是这样的复杂度不知道多快。。
如果是做1~m的话,可以分析
m
l
o
g
m
mlogm
mlogm
但是这里我们只需要做因数,所以不是很会分析,如果分析为
l
o
g
2
log^2
log2那么似乎复杂度太高了呀
看了一下题解的做法:
还是一样的,我们考虑把
d
d
d提出来
那么就变成了
∑
d
d
∑
i
∑
j
(
a
i
,
a
j
)
=
d
\sum_d{d}\sum_i\sum_j{(a_i,a_j)=d}
d∑di∑j∑(ai,aj)=d
这个怎么做呢?
有一个看似很强的转化
[
x
=
d
]
=
∑
d
∣
k
,
k
∣
x
μ
(
k
d
)
[x=d]=\sum_{d|k,k|x}{\mu(\frac{k}{d})}
[x=d]=d∣k,k∣x∑μ(dk)
这个式子其实本质上和
[
x
=
1
]
=
∑
k
∣
x
μ
(
k
)
[x=1]=\sum_{k|x}{\mu(k)}
[x=1]=k∣x∑μ(k)是一样的…
那么套路地,我们把它丢进去
∑
d
d
∑
i
∑
j
∑
d
∣
k
,
k
∣
(
a
i
,
a
j
)
μ
(
k
d
)
\sum_d{d}\sum_i\sum_j\sum_{d|k,k|(a_i,a_j)}{\mu(\frac{k}{d})}
d∑di∑j∑d∣k,k∣(ai,aj)∑μ(dk)
把k提到前面
∑
k
∑
d
∣
k
μ
(
k
d
)
∗
d
∗
(
∑
[
k
∣
a
i
]
)
2
\sum_k\sum_{d|k}\mu(\frac{k}{d})*d*(\sum{[k|a_i])^2}
k∑d∣k∑μ(dk)∗d∗(∑[k∣ai])2
可以发现,对于同一个
k
k
k,中间那个一个是不变的,可以预处理出来
稍加观察可以发现,这个就是欧拉函数,因为
ϕ
(
n
)
n
=
∑
d
∣
n
μ
(
d
)
d
\frac{\phi(n)}{n}=\sum_{d|n}\frac{\mu(d)}{d}
nϕ(n)=d∣n∑dμ(d)
两边同时乘n就可以了
至于右边那个,分解一下质因数,然后开个桶记录一下次数就好了
分解质因数显然可以预处理
然后
f
f
f就求完了
那么复杂度就是因数个数了
n
l
o
g
n
nlogn
nlogn一波就可以吧前面弄出来了
好了,那么我们来分析一下这个的复杂度
显然,每一个数算的次数是
∑
d
(
i
)
∗
d
(
a
i
)
\sum{d(i)*d(a_i)}
∑d(i)∗d(ai)
因为题目有条件:
a
i
a_i
ai是个排列
因此,根据排列不等式,当
a
i
=
i
a_i=i
ai=i的时候复杂度达到最高
那么就是
O
(
n
l
o
g
n
2
)
O(nlogn^2)
O(nlogn2)的了
然后本题就解决了
em…还看到了简短的
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)筛
ϕ
\phi
ϕ和
μ
\mu
μ的方法
以前一直都是线性筛
然后,大概就这样吧
CODE:
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<vector>
using namespace std;
typedef long long LL;
const LL N=100005;
const LL MOD=1e9+7;
LL n;
LL a[N];
LL mu[N],g[N],f[N];
vector<LL> h[N],tmp;//分解
LL tot[N];
int main()
{
scanf("%lld",&n);
for (LL u=1;u<=n;u++) scanf("%lld",&a[u]);
for (LL u=1;u<=n;u++)
for (LL i=u;i<=n;i+=u)
h[i].push_back(u);
for (LL u=1;u<=n;u++)
{
if (u==1) mu[u]=1;
for (LL i=u+u;i<=n;i+=u) mu[i]-=mu[u];
}
for (LL u=1;u<=n;u++)
for (LL i=u;i<=n;i+=u)
g[i]=g[i]+u*mu[i/u];
LL siz,x;
for (LL u=1;u<=n;u++)
{
tmp.clear();
for (LL i=1;i<=n/u;i++)//集合是到a_i
{
x=a[i*u];siz=h[x].size();
for (LL j=0;j<siz;j++)
{
tot[h[x][j]]++;
tmp.push_back(h[x][j]);
}
}
siz=tmp.size();
for (LL i=0;i<siz;i++)
if (tot[tmp[i]]!=0)
{
x=tmp[i];
f[u]=(f[u]+g[x]*tot[x]%MOD*tot[x]%MOD)%MOD;
tot[x]=0;
}
}
LL ans=0;
for (LL d=1;d<=n;d++)
{
for (LL i=1;i<=n/d;i++)
ans=(ans+d*mu[i]%MOD*f[i*d]%MOD)%MOD;
}
ans=(ans+MOD)%MOD;
printf("%lld\n",ans);
return 0;
}