icpc预赛徐州: Easy Math (杜教筛)

原题:ACM-ICPC 2018 徐州赛区网络预赛 Easy Math

题意:

i = 1 m u ( i n )

解析:

如果n有个因子是某个素数的平方,那么根据莫比乌斯函数,答案为0,所以我们考虑其他的情况

设d为n的一个素因子,那么n/d与d互质,而莫比乌斯函数又是积性函数,所以有:

i = 1 m u ( i n ) = i = 1 m u ( i d n d ) = i = 1 m u ( i n d ) u ( d )

而质数的函数值为-1,所以原式变成:

i = 1 m u ( i n d )

但是我们好像漏了一个东西,d和n/d互质,但是只有当d和i也互质的时候才能拆出来,而不能拆出来的部分有哪些呢?d为质数,所以i为d的倍数的时候才不互质,所以答案为:

i [ 1 , m ] d i u ( i n d ) u ( d ) + i [ 1 , m ] d i u ( i n d d )

显然右边部分为0,答案为:

i = 1 m u ( i n d ) + i [ 1 , m ] d i u ( i n d ) = i = 1 m u ( i n d ) + i = 1 m / d u ( i n )

为什么后半部分可以把1/d提到前面去呢?因为后半部分的i本来就只能取d的倍数,而前半部分i范围为1到m,换成1到m/d后就少了很多

接下来令原答案为Ans(n,m),则有:

A n s ( n , m ) = A n s ( n / d , m ) + A n s ( n , m / d )

递归到最后Ans(n,0)显然是0,而Ans(1,m),在n=1时已经取不到d了就不能往下分了,也就是说需要计算以下公式:

A n s ( 1 , m ) = i = 1 m u ( i )
这个其实裸杜教筛就可以了。

代码:

#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int N=10000111;
#define pill pair<LL ,LL>
int u[N];
LL pre[N];
LL pri[N>>1],now;
bool vis[N];
void init(){
    u[1]=1;now=0;
    for(LL i=2;i<N-9;i++){
        if(!vis[i]){
            pri[++now]=i;u[i]=-1;
        }
        for(int j=1;j<=now&&i*pri[j]<N-9;j++){
            vis[i*pri[j]]=1;
            u[i*pri[j]]=-u[i];
            if(i%pri[j]==0){
                u[i*pri[j]]=0;
                break;
            }
        }
    }
    pre[0]=0;
    for(int i=1;i<N-9;i++)pre[i]=u[i]+pre[i-1];
}
unordered_map<LL,LL>M;
LL calculate(LL n){//杜教筛
    if(M.find(n)!=M.end())return M[n];
    if(n<N-9)return pre[n];
    LL ans=1;
    for(LL l=2,r;l<=n;l=r+1){
        r=n/(n/l);
        ans-=(r-l+1ll)*calculate(n/l);
    }
    return M[n]=ans;
}

bool judge(LL n){//判断u(n)是否直接=0
    for(int i=1;pri[i]*pri[i]<=n;i++){
        if(n%pri[i])continue;
        n/=pri[i];
        if(n%pri[i]==0)return false;
    }
    return true;
}

LL Ans(LL n,LL m){
    if(n==1)return calculate(m);
    if(m==0)return 0;
    if(!judge(n))return 0;
    LL d=-1;                           //之前定义为int RE了一上午,气死了
    for(int i=1;pri[i]*pri[i]<=n;i++)
        if(n%pri[i]==0){d=pri[i];break;}
    if(d==-1)d=n;
    return Ans(n,m/d)-Ans(n/d,m);
}

int main(){
    init();
    LL m,n;
    cin>>m>>n;
    if(!judge(n))return 0*printf("0\n");
    return 0*printf("%lld\n",Ans(n,m));
}

猜你喜欢

转载自blog.csdn.net/jk_chen_acmer/article/details/82627763