bzoj 5478: gcd

题意

给出一个长为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) =ddij[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&#x27;}^{\frac{n}{d}}{mu(d&#x27;)}\sum_i\sum_j(a_{idd&#x27;},a_{jdd&#x27;}) =ddddnmu(d)ij(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} ddij(ai,aj)=d
这个怎么做呢?
有一个看似很强的转化
[ x = d ] = ∑ d ∣ k , k ∣ x μ ( k d ) [x=d]=\sum_{d|k,k|x}{\mu(\frac{k}{d})} [x=d]=dk,kxμ(dk)
这个式子其实本质上和 [ x = 1 ] = ∑ k ∣ x μ ( k ) [x=1]=\sum_{k|x}{\mu(k)} [x=1]=kxμ(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})} ddijdk,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} kdkμ(dk)d([kai])2
可以发现,对于同一个 k k k,中间那个一个是不变的,可以预处理出来
稍加观察可以发现,这个就是欧拉函数,因为
ϕ ( n ) n = ∑ d ∣ n μ ( d ) d \frac{\phi(n)}{n}=\sum_{d|n}\frac{\mu(d)}{d} nϕ(n)=dndμ(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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值