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

来源:互联网 发布:按键精灵源码 编辑:程序博客网 时间:2024/06/05 17:28

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, 6481,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的贡献的一倍)

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


b/x+b/(x*2)+b/(x*3)+b/x*4+....算贡献的时候,x如果很小,b很大,那么这个式子是不能一个一个除的,所以用分块的思想,如果b/y和b/(y+x)和b/(y+x*2)和b/(y+x*3)的值一样,到b/(y+x*4)的值减小了,那么我能不能求出有多少个数字的值是和b/y的值是一样的,现在先将这个值算出来是b/y,那么b/(b/y)就是这个b/?这个数字(?)的最大值了。那么二分一下找一个最大的L值

y+L*x<=b/(b/y);用二分的范围无法确定,那么就用倍增思想解决这个问题,找到的最大的L值加上1再乘以(b/y)就是这一块的贡献了。

代码:

#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<queue>#include<string>#include<algorithm>#define inf 0x3f3f3f3f#define LL long longusing namespace std;LL F(int &i,int y,int x)//这里的i是引用的{    int l=0,r=1,mid;    int xx=x/(x/i);    while(i+r*y<=xx)//倍增思想    {        l=r;        r*=2;    }    while(l<r)    {        mid=(l+r+1)/2;        if(i+mid*y<=xx) l=mid;        else r=mid-1;    }    i=i+l*y;//找到最大的符合条件的l值    return (LL)x/i*(l+1);}LL love(int y,int x){    LL sum=0LL;    for(int i=y;i<=x;i+=y)        sum+=F(i,y,x);    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));}





原创粉丝点击