【NOI2010/BZOJ2005】能量采集 莫比乌斯反演

原题走这里

这其实是我的第一道认真做的莫比乌斯反演题

经过观察发现,位于(x,y)的植物的能量损失为gcd(x,y)1
于是我们发现,原题实际上就是在让我们求

i=1nj=1m(2gcd(i,j)1)=2i=1nj=1mgcd(i,j)nm

那么问题来了,我们该怎么求这个式子呢:
i=1nj=1mgcd(i,j)

这时候我们就需要用到懵逼钨丝莫比乌斯反演啦……

莫比乌斯反演,说白了就是两个公式:
如果f(n)=d|ng(d),则g(n)=d|nμ(d)f(nd)
如果f(n)=n|dg(d),则g(n)=n|dμ(dn)f(d)
前者称为约数的莫比乌斯反演,后者称为倍数的莫比乌斯反演。其中的μ(n)是莫比乌斯函数,n包含平方因子时,μ(n)=0,否则μ(n)=(1)n

现在,让我们对原式作一个变形:

ddi=1nj=1m[gcd(i,j)=d]

其中方括号表示括号内式子为真时值为一,否则为零。
现在不妨记g(k)=i=1nj=1m[gcd(i,j)=d]
然后我们可以构造出f(k)=k|dg(d)=i=1nj=1m[k|gcd(i,j)]
这时候,我们惊讶地发现:f(k)=nkmk
于是此时,我们就可以使用倍数的莫比乌斯反演,Excalibur——————!!!
g(k)=k|dμ(dk)f(d)=k|dμ(dk)ndmd

暴力累加就可以了
记得用long long

什么?你问我怎么算μ(n)

具体实现见代码如下:

#include <bits/stdc++.h>
#define LL long long
using namespace std;
int n,m,p[100000],p_cnt,Mu[1000000];
bool flag[1000000];
void init()//线性筛求莫比乌斯函数
{
    Mu[1]=1;
    for(int i=2;i<=n;i++)
    {
        if(!flag[i])
        {
            Mu[i]=-1;
            p[++p_cnt]=i;
        }
        for(int j=1;j<=p_cnt&&i*p[j]<=n;j++)
        {
            flag[i*p[j]]=1;
            if(i%p[j]==0)
            {
                Mu[i*p[j]]=0;
                break;
            }
            Mu[i*p[j]]=-Mu[i];
        }
    }
}
LL solve()
{
    LL ret=0;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j*i<=n;j++)
        {
            ret+=1LL*i*Mu[j]*(n/(j*i))*(m/(j*i));
        }
    }
    return ret;
}
int main()
{
    cin>>n>>m;
    if(m<n)swap(n,m);
    init();
    cout<<(solve()*2-1LL*n*m)<<endl;
    return 0;
}
发布了45 篇原创文章 · 获赞 20 · 访问量 6783
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 编程工作室 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览