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

原题走这里

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

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

i=1nj=1m(2gcd(i,j)1)=2i=1nj=1mgcd(i,j)nm ∑ i = 1 n ∑ j = 1 m ( 2 ∗ g c d ( i , j ) − 1 ) = 2 ∗ ∑ i = 1 n ∑ j = 1 m g c d ( i , j ) − n ∗ m

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

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

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

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

ddi=1nj=1m[gcd(i,j)=d] ∑ d d ∑ i = 1 n ∑ j = 1 m [ g c d ( i , j ) = d ]

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

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

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

具体实现见代码如下:

#include 
#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(mm);
    init();
    cout<<(solve()*2-1LL*n*m)<return 0;
}

你可能感兴趣的:(【NOI2010/BZOJ2005】能量采集 莫比乌斯反演)