NOIP提高组【JZOJ4782】Math

本文介绍了一种利用线性筛法解决数学问题的方法,重点在于如何快速计算一个数的特定质因子的连乘积,并通过此来确定完全平方数的个数。文章详细解释了算法原理并给出了具体的实现代码。

Description

这里写图片描述

Data Constraint

这里写图片描述

Solution

考虑到是(1)mj=1d(ij),所以我们要考虑mj=1d(ij)的奇偶性。很显然,只有当i* j为完全平方数时,d(i* j)Mod2才为1,其余Mod2后都为0。我们分解质因数设i=ap11ap22…… apkk,设p表示当p*i使p *i为完全平方数时,p最小的值为多少。我们设p=ab11ab22…… abkk那么显然p要使每一个p[i]+b[i]均为偶数,且b[i]尽量小。所以,当p[i]为奇数时,b[i]就为1,当p[i]为偶数时,b[i]就为0。那么我们就可以很快算出p的值了。

得到p后,我们考虑要怎样的j才时i*j为完全平方数。那么显然j=p*q2,q为任意数且p*q2<=m。那么我们可以发现q的取值个数为m/p。所以,我们只要判断一下m/p的奇偶性。m/p为奇数则答案+1,否则答案-1。那么,这道题我们就可以用O(N*N)的时间看似解决了。

但其实,这会超时……(N<=107)。

问题就出在计算p上。我们要计算一个数的的指数为奇数的的质因子的连乘时是O(N)的。所以我们必须用更高级的算法!

对,就是线性筛。

设一个数i的指数为奇数的质因子的连乘是f[i],那么对于一个质数,它的f值显然是i。那么对于一个i*d[k] (d[k]表示小于等于i的质数),当i不是d[k]的倍数时,表明i中不含有d[k]这个因子,那么f[i *d[k]]显然等于f[i] *d[k]。当i是d[k]的倍数时,我们还要考虑f[i]中是否已经含有d[k]。(因为不是i是d[k]的倍数并不能说明d[k]在i中的指数为奇数!)假设f[i]中已经含有d[k],那么f[i *d[k]]中则要把d[k]删去,即f[i *d[k]]=f[i]/d[k],否则f[i *d[k]]中则要把d[k]加上,即f[i *d[k]]=f[i] *d[k]。

最后O(N)扫一遍f统计一下答案即可。总时间复杂度为O(N)。(看起来解释很复杂,其实代码很短的……)

代码

#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int maxn=10000007;
int f[maxn],n,i,t,j,k,l,x,y,z;
ll m,d[maxn],p,ans;
bool bz[maxn],bz1;
int main(){
//  freopen("data.in","r",stdin);
    scanf("%d%lld",&n,&m);
    f[1]=1;
    for (i=2;i<=n;i++){
        if (!bz[i]) d[++d[0]]=i,f[i]=i;
        for (k=1;k<=d[0];k++){
            if (i*d[k]>n) break;bz[i*d[k]]=true;
            if (i%d[k]) f[i*d[k]]=f[i]*d[k];
            else{
                if (f[i]%d[k])f[i*d[k]]=f[i]*d[k];
                else f[i*d[k]]=f[i]/d[k];
                break;
            }
        }
    }
    for (i=1;i<=n;i++){
        t=sqrt(m/f[i]);
        if (t%2) ans--;
        else ans++;
    }
    printf("%d\n",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值