hdu 4746 Mophues (莫比乌斯反演 + 分块+线性筛)

注意:

因为是多组询问(5000)而n又高达5e5,所以我们每次的时间一定要控制在logn或sqrt(n)内才能1s过,分块是必须的了。

然后是一个坑点:必须在线性筛里统计数量,自己写函数一个个统计会t在这上。

跟csu 1325的思路类似:需要预处理出 Fi 的系数,然后求前缀和分块。然而这题p的值会影响某些 Fi 的系数,所以我们需要将不同的p值的前缀和分别保存下来。注意此题当p大于19时,最小值也是1e6,所以直接输出m*n就好。我们预处理p从0到19的前缀和,注意到当p很大时,所有的 fi 都会被统计到 Fi 中,所以我们从小到大处理就好。第二个代码的预处理思路类似,然而可能是由于局部性原理的原因,慢了100ms左右。
http://blog.csdn.net/wing_wuchen/article/details/76861929

249ms的代码:

#include<stdio.h>
#include <iostream>
#include<string.h>
#include<math.h>
#include<algorithm>
#define eps 1e-8
typedef long long int lli;
using namespace std;

const int maxn = 5e5+20;

bool isprime[maxn];
//int phi[maxn];
int prime[maxn],miu[maxn];
int cnt[maxn];
void moblus(){
    int num = 0;miu[1] = 1;
    for(lli i = 2;i < maxn;i++){
        if(!isprime[i]){
            prime[num++] = i,miu[i] = -1;//phi[i] = i-1;
            cnt[i] = 1;
        }
        for(lli j = 0;j < num && i*prime[j] < maxn;j++){
            lli x = prime[j];
            isprime[i*x] = 1;cnt[i*x] = cnt[i]+1;
            if(i%x){
                miu[i*x] = -miu[i];
                //phi[i*x] = phi[i] * phi[x];
            }
            else{
                miu[i*x] = 0;
                //phi[i*x] = phi[i] * x;
                break;
            }
        }
    }
}



int ff[maxn];bool v[maxn];
int sum[25][maxn];

int main(){
    moblus();
    for(int p = 0;p <= 19;p++){
        for(int i = 1;i < maxn;i++){
            if(cnt[i] > p || v[i]) continue;
            v[i] = true;
            for(int j = 1;j*i < maxn;j++){
                ff[i*j] += miu[j];
            }
        }
        for(int i = 1;i < maxn;i++){
            sum[p][i] = sum[p][i-1]+ff[i];
        }
    }
    int q,n,m,p;
    scanf("%d",&q);
    while(q--){
        scanf("%d%d%d",&n,&m,&p);
        if(p > 19){
            printf("%lld\n",(lli)m*(lli)n);
            continue;
        }
        if(n>m) swap(n,m);
        lli ans = 0,l;
        for(int i = 1;i <= n;i=l+1){
            l = min(n/(n/i),m/(m/i));
            ans += (lli)(sum[p][l]-sum[p][i-1])*(lli)(n/i)*(lli)(m/i);
        }
        printf("%lld\n",ans);
    }

    return 0;
}

390ms的代码:

#include<stdio.h>
#include <iostream>
#include<string.h>
#include<math.h>
#include<algorithm>
#define eps 1e-8
typedef long long int lli;
using namespace std;

const int maxn = 5e5+20;
bool isprime[maxn];
//int phi[maxn];
int prime[maxn],miu[maxn];
int cnt[maxn];
void moblus(){
    int num = 0;miu[1] = 1;
    for(lli i = 2;i < maxn;i++){
        if(!isprime[i]){
            prime[num++] = i,miu[i] = -1;//phi[i] = i-1;
            cnt[i] = 1;
        }
        for(lli j = 0;j < num && i*prime[j] < maxn;j++){
            lli x = prime[j];
            isprime[i*x] = 1;cnt[i*x] = cnt[i]+1;
            if(i%x){
                miu[i*x] = -miu[i];
                //phi[i*x] = phi[i] * phi[x];
            }
            else{
                miu[i*x] = 0;
                //phi[i*x] = phi[i] * x;
                break;
            }
        }
    }
}
int sum[25][maxn];

int main(){
    moblus();
    for(int i = 1;i < maxn;i++){
        for(int j = 1;j*i < maxn;j++){
            sum[cnt[i]][i*j] += miu[j];//我觉得可能是这里 内存的局部性原理导致100ms的延迟吧
        }
    }
    for(int i = 1;i < maxn;i++){
        sum[0][i] = sum[0][i-1]+sum[0][i];
    }
    for(int p = 1;p <= 19;p++){
        for(int i = 1;i < maxn;i++){
            sum[p][i] = sum[p][i-1] + sum[p][i];
        }
        for(int i = 1;i < maxn;i++){
            sum[p][i] += sum[p-1][i];
        }
    }
    int q,n,m,p;
    scanf("%d",&q);
    while(q--){
        scanf("%d%d%d",&n,&m,&p);
        if(p > 19){
            printf("%lld\n",(lli)m*(lli)n);
            continue;
        }
        if(n>m) swap(n,m);
        lli ans = 0,l;
        for(int i = 1;i <= n;i=l+1){
            l = min(n/(n/i),m/(m/i));
            ans += (lli)(sum[p][l]-sum[p][i-1])*(lli)(n/i)*(lli)(m/i);
        }
        printf("%lld\n",ans);
    }

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值