题意
给出一个长为N的数列a,求
∑∑(ai,aj)?(i,j)\sum\sum(a_i,a_j)*(i,j)∑∑(ai?,aj?)?(i,j)
1<=i<=N,1<=j<=N
bzoj
loj
题解
看到gcd,一半都要想到莫比乌斯反演
先套路地尝试搞掉一个
原式=∑dd∑i∑j[i,j]=d?(ai,aj)原式=\sum_d{d}\sum_i\sum_j{[i,j]=d}*(a_i,a_j)原式=d∑?di∑?j∑?[i,j]=d?(ai?,aj?)
=∑dd∑d′ndmu(d′)∑i∑j(aidd′,ajdd′)=\sum_{d}{d}\sum_{d'}^{\frac{n}{d}}{mu(d')}\sum_i\sum_j(a_{idd'},a_{jdd'})=d∑?dd′∑dn??mu(d′)i∑?j∑?(aidd′?,ajdd′?)
似乎没什么好化的了
观察后面哪一个东西,等价于一个集合里面,所有数两两的gcdgcdgcd和
那么对于每一次,我们把这个集合拿出来,设为SSS
定义f(S)f(S)f(S)就是这个两两的gcdgcdgcd和
em…
一个比较显然的做法是直接套上莫比乌斯反演
也就是先得到F(i)F(i)F(i)表示gcd是i的倍数的答案
然后再用莫比乌斯返回去
但是这样的复杂度不知道多快。。
如果是做1~m的话,可以分析mlogmmlogmmlogm
但是这里我们只需要做因数,所以不是很会分析,如果分析为log2log^2log2那么似乎复杂度太高了呀
看了一下题解的做法:
还是一样的,我们考虑把ddd提出来
那么就变成了∑dd∑i∑j(ai,aj)=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μ(kd)[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)是一样的…
那么套路地,我们把它丢进去
∑dd∑i∑j∑d∣k,k∣(ai,aj)μ(kd)\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μ(kd)?d?(∑[k∣ai])2\sum_k\sum_{d|k}\mu(\frac{k}{d})*d*(\sum{[k|a_i])^2}k∑?d∣k∑?μ(dk?)?d?(∑[k∣ai?])2
可以发现,对于同一个kkk,中间那个一个是不变的,可以预处理出来
稍加观察可以发现,这个就是欧拉函数,因为
?(n)n=∑d∣nμ(d)d\frac{\phi(n)}{n}=\sum_{d|n}\frac{\mu(d)}{d}n?(n)?=d∣n∑?dμ(d)?
两边同时乘n就可以了
至于右边那个,分解一下质因数,然后开个桶记录一下次数就好了
分解质因数显然可以预处理
然后fff就求完了
那么复杂度就是因数个数了
nlognnlognnlogn一波就可以吧前面弄出来了
好了,那么我们来分析一下这个的复杂度
显然,每一个数算的次数是 ∑d(i)?d(ai)\sum{d(i)*d(a_i)}∑d(i)?d(ai?)
因为题目有条件:aia_iai?是个排列
因此,根据排列不等式,当ai=ia_i=iai?=i的时候复杂度达到最高
那么就是O(nlogn2)O(nlogn^2)O(nlogn2)的了
然后本题就解决了
em…还看到了简短的O(nlogn)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;
}