51Nod 欢乐手速场1 C 开心的小Q[莫比乌斯函数]

时间:2022-06-28 19:23:32
tangjz (命题人)
quailty (测试)
 
基准时间限制:1 秒 空间限制:131072 KB 分值: 80
如果一个数字存在一个约数是完全平方数,那么小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

标解:http://www.51nod.com/contest/problemSolution.html#!problemId=1742 

里面的式子改写一开始一直不明白,稍微有点明白了,说说另一种想的方法吧
最暴力的是枚举数字再枚举约数看看是不是平方因子数了
一个简单的优化是枚举约数i,约数i的贡献(倍数的数量)就是[n/i]
但还是会T
一个巧妙的想法是我们枚举是几倍,当前枚举的是i倍,那么满足i倍<=n的数字就是<=[n/i],答案加上<=[n/i]的平方因子数的个数,就是标解中最后的式子了
计算的时候用了两次分块n/(i*i)和n/i做到O(n^2/3)
然而比只一次分块还慢是什么鬼?sqrt的问题吗
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
typedef long long ll;
const int N=1e5+;
inline int read(){
char c=getchar();ll x=,f=;
while(c<''||c>''){if(c=='-')f=-;c=getchar();}
while(c>=''&&c<=''){x=x*+c-'';c=getchar();}
return x*f;
}
int a,b;
bool notp[N];
int p[N],mu[N];
void sieve(int n){
mu[]=;
for(int i=;i<=n;i++){
if(!notp[i]) p[++p[]]=i,mu[i]=-;
for(int j=;j<=p[]&&i*p[j]<=n;j++){
notp[i*p[j]]=;
if(i%p[j]==) break;
mu[i*p[j]]=-mu[i];
}
}
//for(int i=1;i<=n;i++) printf("mu %d %d\n",i,mu[i]);
//for(int i=1;i<=n;i++) mu[i]+=mu[i-1];
}
//int F(int n){
// int _=0,r=0,m=sqrt(n);
// for(int i=1;i<=m;i=r+1){
// r=n/(n/(i*i));
// _+=n/(i*i)*(mu[r]-mu[i-1]);
// }
// return n-_;
//}
int f(int n){
int _=,m=sqrt(n);
for(int i=;i<=m;i++)
_+=n/(i*i)*mu[i];
return n-_;
}
ll S(int n){//printf("n %d\n",n);
ll ans=;int r=;
for(int i=;i<=n;i=r+){
r=n/(n/i);
ans+=(r-i+)*f(n/i);
//printf("f %d %d %d %d\n",n/i,i,r,f(n/i));
}
return ans;
}
int main(){
//freopen("in","r",stdin);
a=read();b=read();
sieve(sqrt(b)+);
printf("%lld",S(b)-S(a-));
}

一次分块


#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
typedef long long ll;
const int N=1e5+;
inline int read(){
char c=getchar();ll x=,f=;
while(c<''||c>''){if(c=='-')f=-;c=getchar();}
while(c>=''&&c<=''){x=x*+c-'';c=getchar();}
return x*f;
}
int a,b;
bool notp[N];
int p[N],mu[N];
void sieve(int n){
mu[]=;
for(int i=;i<=n;i++){
if(!notp[i]) p[++p[]]=i,mu[i]=-;
for(int j=;j<=p[]&&i*p[j]<=n;j++){
notp[i*p[j]]=;
if(i%p[j]==) break;
mu[i*p[j]]=-mu[i];
}
}
//for(int i=1;i<=n;i++) printf("mu %d %d\n",i,mu[i]);
for(int i=;i<=n;i++) mu[i]+=mu[i-];
}
int F(int n){//printf("F %d\n",n);
int _=,r=,m=sqrt(n);
for(int i=;i<=m;i=r+){
r=sqrt(n/(n/(i*i)));//printf("r %d\n",r);
_+=n/(i*i)*(mu[r]-mu[i-]);
}
return n-_;
}
ll S(int n){//printf("n %d\n",n);
ll ans=;int r=;
for(int i=;i<=n;i=r+){
r=n/(n/i);
ans+=(r-i+)*F(n/i);
//printf("F %d %d %d %d\n",n/i,i,r,F(n/i));
}
return ans;
}
int main(){
//freopen("in","r",stdin);
a=read();b=read();
sieve(sqrt(b)+);
printf("%lld",S(b)-S(a-));
}