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

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+5;
inline int read(){
    char c=getchar();ll x=0,f=1;
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
    return x*f;
}
int a,b;
bool notp[N];
int p[N],mu[N];
void sieve(int n){
    mu[1]=1;
    for(int i=2;i<=n;i++){
        if(!notp[i]) p[++p[0]]=i,mu[i]=-1;
        for(int j=1;j<=p[0]&&i*p[j]<=n;j++){
            notp[i*p[j]]=1;
            if(i%p[j]==0) break;
            mu[i*p[j]]=-mu[i];
        }
    }
    //for(int i=1;i<=n;i++) printf("mu %d %d
",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 _=0,m=sqrt(n);
    for(int i=1;i<=m;i++)
        _+=n/(i*i)*mu[i];
    return n-_;
}
ll S(int n){//printf("n %d
",n);
    ll ans=0;int r=0;
    for(int i=1;i<=n;i=r+1){
        r=n/(n/i);
        ans+=(r-i+1)*f(n/i);
        //printf("f %d  %d %d  %d
",n/i,i,r,f(n/i));
    }
    return ans;
}
int main(){
    //freopen("in","r",stdin);
    a=read();b=read();
    sieve(sqrt(b)+1);
    printf("%lld",S(b)-S(a-1));
}
一次分块

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





原文地址:https://www.cnblogs.com/candy99/p/6367375.html