基准时间限制: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));
}