bzoj2226 [Spoj 5971] LCMSum(反演(互质数之和的公式))

Description

Given n, calculate the sum LCM(1,n) + LCM(2,n) + .. + LCM(n,n), where LCM(i,n) denotes the Least Common Multiple of the integers i and n.

Input

The first line contains T the number of test cases. Each of the next T lines contain an integer n.

Output

Output T lines, one for each test case, containing the required sum.

Sample Input

3

1

2

5

Sample Output

1

4

55

HINT

1 <= T <= 300000

1 <= n <= 1000000

[ Submit][ Status][ Discuss]


分析:

感觉牵扯到LCM的式子都比较难化

这里写图片描述

这里我稍微解释一下这个式子:
这里写图片描述

现在我们的问题就是求解:
这里写图片描述

设:
这里写图片描述

这个式子的定义就是:1~n中所有与n互质的数之和

关于这个问题,有一个公式(这里就不推导了):
这里写图片描述

其中g(1)=1

(请各位做好笔记,直接记住这个公式)

这里写图片描述

枚举约数d的复杂度为sqrt(n)

#include<cstdio>
#include<cstring>
#include<iostream>
#define ll long long

using namespace std;

const int N=1000003;
ll phi[N];
int sshu[N],tot=0,n;
bool no[N];

void make()
{
    for (int i=2;i<N;i++)
    {
        if (!no[i])
            sshu[++tot]=i;
        for (int j=1;j<=tot&&sshu[j]*i<N;j++)
        {
            no[sshu[j]*i]=1;
            if (i%sshu[j]==0) break;
        }
    }

    for (int i=1;i<N;i++) phi[i]=(ll)i;
    for (int i=1;i<=tot;i++)
        for (int j=sshu[i];j<N;j+=sshu[i])
        {
            phi[j]=(ll)phi[j]/sshu[i];
            phi[j]=(ll)phi[j]*(sshu[i]-1);
        }
    for (int i=2;i<N;i++)
        phi[i]=(ll)phi[i]*i/2;
}

int main()
{
    make();
    int T;
    scanf("%d",&T);
    while (T--)
    {
        scanf("%d",&n);
        ll ans=0;
        for (int i=1;i*i<=n;i++)
            if (n%i==0)
            {
                ans=ans+phi[n/i];
                if (n/i!=i) ans=ans+phi[i];
            }
        printf("%lld\n",(ll)n*ans);     //不要忘了*n 
    }
    return 0;
}   

把上面的代码交上去,险些T掉

因为题目中的询问非常多,我们应该有一个意识:把答案组织成便于回答询问的形式

实际上我们不用每读入一个n之后再枚举ta的约数
我们可以在一开始就预处理好,这样就可以O(1)回答询问了
(我们还可以把phi的计算放到线性筛中,进一步优化时间)

这里写图片描述

(我的代码竟然以微弱优势踩了GXZlegend)

void make()
{
    phi[1]=1;
    for (int i=2;i<N;i++)
    {
        if (!no[i])
        {
            sshu[++tot]=i;
            phi[i]=(ll)i-1;    //素数 
        }
        for (int j=1;j<=tot&&sshu[j]*i<N;j++)
        {
            no[sshu[j]*i]=1;
            if (i%sshu[j]==0)
            {
                phi[i*sshu[j]]=(ll)phi[i]*sshu[j];   //积性函数 
                break;
            }
            phi[i*sshu[j]]=(ll)phi[i]*phi[sshu[j]];
        }
    }

    for (int i=2;i<N;i++)
        phi[i]=(ll)phi[i]*i/2;
}

ll ans[N];

int main()
{
    make();
    for (int i=1;i<N;i++)
        for (int j=i;j<N;j+=i)
            ans[j]+=phi[i];

    int T;
    scanf("%d",&T);
    while (T--)
    {
        scanf("%d",&n);
        printf("%lld\n",n*ans[n]);
    }
    return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值