51nod 1742 开心的小Q (容斥,分块)

 

1742 开心的小Q

基准时间限制:1 秒 空间限制:131072 KB 分值: 80 难度:5级算法题


 

如果一个数字存在一个约数是大于1的完全平方数,那么小Q就认为这个数是有趣的。小Q喜欢收集有趣的数字,每找到一个有趣的数,小Q就会变得很开心。

小Q发现12是有趣的,18也是有趣的,它们都是36的约数,而在36的约数中,还有3个数是有趣的,它们是4、9、36。

小Q很好奇,在a~b里每个数字各有多少个有趣的约数,由于a和b太大了,所以他只想知道这些个数之和是多少。

例如4有1个有趣的约数,8有2个有趣的约数,9有1个有趣的约数,所以1~10里每个数的有趣约数个数之和是4。

Input

输入数据包括2个数:a, b,中间用空格分隔。(1≤a≤b≤10^9)

Output

输出a~b里每个数字的有趣约数个数之和。

Input示例

1 10
Output示例
4

 

 

题意:汉语题意就不在描述了。

思路:先求1到b,在求出1到a-1,然后做差。

任意一个完全平方数的倍数如果小于b,那么这个完全平方数的贡献就会加一次。那么这个完全平方数x的总贡献为b/x+b/(x*2)+b/(x*3)+b/x*4+....直到x的倍数大于b为止(这个地方要用分块会快一点)。所有完全平方数的贡献之和就是答案,但是有一点,36是完全平方数,4,9也是完全平方数,那么在计算4和9的倍数的时候,36被算了两次,那么这样有些数字被多次计算,所以要用容斥定理解决这个问题。

首先将所有小于b的完全平方数求出来,

4,9,16,25,36,49,64,81,100,121,144,169,196.......然后

1, 1 , 1, 1, 1,  1,1,  1,    1,    1,   1,    1,   1......这些数字是表示应该加上(这个完全平方数的贡献)的倍数。

计算完4以后,那么4的所有的(完全平方数)倍数,全部多算了1个,那么时4的(完全平方数)倍数所要计算的次数全部减一,减完后计算次数如下:

 

4,9,   16,  25,  36,  49,  64,81,100,121,144,169,196......

1, 1 , 1-1,   1, 1-1,   1,1-1,  1  ,1-1,   1 , 1-1,  1 , 1-1......

然后计算9,那么9的所有(完全平方数)倍数全部减去9算的次数

4,9,   16,  25,  36,  49,  64,81,100,121,144,169,196........

1, 1 ,    0,   1,  0-1,   1,   0,1-1,   0,   1 ,0-1,  1 ,   0.......

然后16为0就不用算了,然后算25的

 

4,9,   16,  25,  36,  49,  64,81,100,121,144,169,196........

1, 1 ,    0,   1,  -1,   1,   0,    0 , 0-1,   1 ,-1,   1 ,    0.......

然后算36,36的倍数是-1,(这是因为4,和9都算了36,所以多算了一遍,所以要减去36的贡献的一倍)

然后就这样一直算下去,算完所有的完全平方数的贡献即可。

x/y+x/(y*2)+x/(y*3)+x/y*4+....=(x/y)/1+(x/y)/2+(x/y)/3+(x/y)/4+....算贡献的时候(x为题目输入的b,y为i*i),用分块求即可

代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<queue>
#include<string>
#include<algorithm>
#define inf 0x3f3f3f3f
#define LL long long
using namespace std;
LL love(int y,int x)
{
    LL sum=0LL;
    x=x/y;
    for(int i=1,j;i<=x;i=j+1)
    {
        j=x/(x/i);
        sum+=(j+1-i)*(x/i);
    }
    return sum;
}
int p[100000];
LL slove(int x)
{
    memset(p,0,sizeof(p));
    int lx=0;
    for(int i=2;i*i<=x;i++)
    {
        p[i]=1;//存该数字要计算的贡献的倍数
        lx=i;//存最大的i
    }
    LL sum=0;
    for(int i=2;i<=lx;i++)
    {
        if(!p[i]) continue;
        sum+=p[i]*love(i*i,x);
        for(int j=i*2;j<=lx;j+=i)
            p[j]-=p[i];//更新倍数
    }
    return sum;
}
int main()
{
    int a,b;
    scanf("%d%d",&a,&b);
    printf("%I64d\n",slove(b)-slove(a-1));
}

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值