[2018.07.10 T3]数论题

暂无链接

数论题

【问题描述】

企鹅国数学家 QQ 潜心研究数论,终于发现了一个简单的数论问题!

一个 QQ 数定义为一个拥有一个大于 1 的完全平方数为因子的数字,一个数字的QQ 值定 义为这个数是 QQ 数的因数个数。

现在 QQ 想知道在[L,R]范围内,每个整数的 QQ 值之和是多少?

你只需要告诉他这个数字,他就可以给你宝贵的 10 分作为一个奖励!

【输入格式】

第一行两个整数 L, R 代表要求的数字范围;

【输出格式】

输出一个整数表示 L~R 里每个数字的 QQ 值之和。

【输入样例】

1 10

【输出样例】

4

【样例说明】

4 的QQ 值为 1,8 的QQ 值为 2,9 的QQ 值为 1。

【数据范围】

对于 10%的数据,R≤10^4;
对于另外 30%的数据,R≤10^6;
对于另外 10%的数据,R≤10^7;
对于 100%的数据,1≤L≤R≤10^9;

题解

从一眼看出要搞 μ μ 之后,似乎没有什么实质进展,然而我有梦想啊!!!

After 3 hours... A f t e r   3   h o u r s . . .

呵呵, 40 40 分滚粗。

虽然看出了 μ(i)=0 μ ( i ) = 0 的就是 QQ Q Q 数,然而感觉没有什么好的表示法,线筛 QQ Q Q 值的梦想也破灭了,唉我的数论还是太菜了。。。

为啥我想不到 1μ2(i) 1 − μ 2 ( i ) 来表示 QQ Q Q 数啊??? sht s h ∗ t

这样以后,就能列出初始的式子:

i=1np|i[1μ2(p)] ∑ i = 1 n ∑ p | i [ 1 − μ 2 ( p ) ]

对于枚举约数的操作,我们可以替换成枚举倍数,这两个枚举是等价的,并且当因子为 QQ Q Q 数时,倍数也一定为 QQ Q Q 数,所以可以化简如下:

i=1nni[1μ2(i)] ∑ i = 1 n ⌊ n i ⌋ [ 1 − μ 2 ( i ) ]

注意到 ni ⌊ n i ⌋ 是可以用下底分块解决的,那么我们只要处理出 ni=1[1μ2(i)] ∑ i = 1 n [ 1 − μ 2 ( i ) ] ,即 [1,n] [ 1 , n ] QQ Q Q 数的个数,这个当然可以线筛,不过只能过 1e7 1 e 7 的,实际上 QQ Q Q 数的个数还可以表示如下:

i=1n[1μ2(i)]=n22+n32+n52n62+... ∑ i = 1 n [ 1 − μ 2 ( i ) ] = ⌊ n 2 2 ⌋ + ⌊ n 3 2 ⌋ + ⌊ n 5 2 ⌋ − ⌊ n 6 2 ⌋ + . . .

含义很简单,就是枚举所有非 QQ Q Q 数的平方的倍数,非 QQ Q Q 的平方必定是 QQ Q Q 数,平方的倍数肯定也是 QQ Q Q 数,但是一个数会被它的因数给算重,所以需要套个 μ μ 去重,最后的形式如下:

i=2nμ(i)ni2 ∑ i = 2 n μ ( i ) ⌊ n i 2 ⌋

注意,因为我们枚举的是非 QQ Q Q 数作为平方根( QQ Q Q 数本身的 μ μ 值为 0 0 ,直接被消掉了),所以枚举上界为n

这样复杂度就对了,愉快的 AC A C

代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int M=1e6+5;
int miu[M],p[M],mx,le,ri;
bool isp[M];
void get()
{
    int i,j,t;
    miu[1]=1;
    for(i=2;i<=mx;++i)
    {
        if(!isp[i])p[++p[0]]=i,miu[i]=-1;
        for(j=1;j<=p[0];++j)
        {
            t=i*p[j];if(t>mx)break;
            isp[t]=1;
            if(i%p[j]==0){miu[t]=0;break;}
            miu[t]=-miu[i];
        }
    }
}
ll miu2(int x)
{
    int b=sqrt(x);ll ans=0;
    for(int i=2;i<=b;++i)ans-=miu[i]*(x/i/i);
    return ans;
}
ll calc(int x)
{
    ll ans=0,tmp,pre=0;
    for(int l=1,r;l<=x;l=r+1){r=x/(x/l);tmp=miu2(r);ans+=1ll*(tmp-pre)*(x/l);pre=tmp;}
    return ans;
}
void in(){scanf("%d%d",&le,&ri);}
void ac(){mx=sqrt(ri);get();printf("%lld",calc(ri)-calc(le-1));}
int main(){in();ac();}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ShadyPi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值