zoukankan      html  css  js  c++  java
  • [2020HDU多校第九场][HDU 6868][B. Absolute Math]

    又被学弟们爆锤了TAT感觉可以原地退役了_(:з」∠)_

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6868

    题目大意:设(f(n)=sum_{d|n} |mu(d)|),求(sum_{i=1}^{m} f(ni))

    题解:分析(f(n))的性质,设(n=prod_{i=1}^{k} p_i^{q_i}),(omega = prod_{i=1}^{k} p_i ),显然当且仅当(d|omega)时存在贡献,而这样的(d)一共有(2^k)个。故若我们设(k(n))为(n)的不同质因子个数,则(f(n)=2^{k(n)}),是一个积性函数

       由于(k(n)=k(omega)),所以有(f(n)=f(omega)),进一步地我们可以得出,(sum_{i=1}^{m} f(ni)=sum_{i=1}^{m} f(omega i))。令(S(n,m)=sum_{i=1}^{m} f(ni)),则有(S(n,m)=S(omega,m))

       注意到(omega)是不同质数的乘积,因此(f(omega i)=f(i)cdot f(frac{omega}{gcd(i,omega)})),就有$$S(n,m)=S(omega,m)=sum_{i=1}^{m} f(omega i)=sum_{i=1}^{m} f(i)cdot f(frac{omega}{gcd(i,omega)})$$

       枚举(d=gcd(i,omega)),得到$$S(omega,m)=sum_{d|omega}f(frac{omega}{d})sum_{i=1}^{lfloor frac{m}{d} floor}f(id)[gcd(i,frac{omega}{d})=1]$$

       由(sum_{d|n}mu(d)=[n==1])$$S(omega,m)=sum_{d|omega}f(frac{omega}{d})sum_{i=1}^{lfloor frac{m}{d} floor}f(id)sum_{e|gcd(i,frac{omega}{d})}mu(e)$$

       交换求和次序,则答案为$$sum_{d|omega}f(frac{omega}{d})sum_{e|frac{omega}{d}}mu(e)sum_{i=1}^{lfloor frac{m}{de} floor}f(ide)$$

       设(T=dcdot e),则有(T|omega),再次交换求和次序,得出答案为$$sum_{T|omega}sum_{i=1}^{lfloor frac{m}{T} floor}f(iT)sum_{d|T}mu(d)f(frac{omega}{frac{T}{d}})=sum_{T|omega}S(T,lfloor frac{m}{T} floor)sum_{d|T}mu(d)f(dcdot frac{omega}{T})$$

       再次注意到(omega)是不同质数的乘积,因此有对任意(T|omega),(gcd(T,frac{omega}{T})=1),而由于(d|T),故(f(dcdot frac{omega}{T})=f(d)f(frac{omega}{T})),因此有$$S(omega,m)=sum_{T|omega}S(T,lfloor frac{m}{T} floor)sum_{d|T}mu(d)f(d)f(frac{omega}{T})=sum_{T|omega}f(frac{omega}{T})S(T,lfloor frac{m}{T} floor)sum_{d|T}mu(d)f(d)$$

       其中(f)的值可以(O(n))线性筛预处理,(S)可以递归求解,现在问题就在于求(sum_{d|T}mu(d)f(d))的值

       注意到这边(T)也是若干个不同质数的乘积,而(f(d)=2^{k(d)}, mu(d)=(-1)^{k(d)}),考虑对所有相同的(k)进行求和,则有(sum_{d|T}mu(d)f(d)=sum_{i=0}^{k(T)}C_{k(T)}^{i}2^icdot(-1)^i),发现这是一个类似于二项式展开的形式,计算得出$$sum_{d|T}mu(d)f(d)=sum_{i=0}^{k(T)}C_{k(T)}^{i}2^icdot(-1)^{k(T)-i}cdot(-1)^{-k(T)+2i}=(-1)^{k(T)}cdot (2+(-1))^{k(T)}=(-1)^{k(T)}cdot 1=mu(T)$$

       于是我们就得出了最终的式子$$S(n,m)=S(omega,m)=sum_{T|omega}mu(T)f(frac{omega}{T})S(T,lfloor frac{m}{T} floor)$$

       得到这个式子后直接递归求解即可,最坏情况下(n)是等于最小的八个质数的乘积(9699690),这种情况下在第一层最多也只会遍历到(256)个不同的(S(n,m))。赛中直接交跑了不到三秒就过了,赛后再交发现会TLE,加了些常数优化卡过去了_(:з」∠)_(赛中赛后运行时间差了整整一倍可还行)

    赛中过的代码

    #include<bits/stdc++.h>
    using namespace std;
    #define N 10000001
    #define LL long long
    #define MOD 1000000007
    int T,n,f[N],sf[N],mu[N],v[N],p[N],cnt;
    void pretype()
    {
        int mx=0;
        v[1]=f[1]=mu[1]=1;
        for(int i=2;i<N;i++){
            if(!v[i]){p[++cnt]=i,mu[i]=-1,f[i]=2,v[i]=i;}
            for(int j=1;j<=cnt && 1ll*i*p[j]<N;j++){
                v[i*p[j]]=v[i]*p[j];
                if(i%p[j]==0){f[i*p[j]]=f[i],v[i*p[j]]=v[i];break;}
                mu[i*p[j]]=-mu[i];
                f[i*p[j]]=2*f[i];
            }
        }
        for(int i=1;i<N;i++)sf[i]=(sf[i-1]+f[i])%MOD;
    }
    int S(int n,int m)
    {
        if(m==0)return 0;
        if(m==1)return f[n];
        if(n==1)return sf[m];
        int res=0;
        for(int d=1;d*d<=n;d++)if(n%d==0){
            res=(res+MOD+1ll*f[n/d]*S(d,m/d)%MOD*mu[d])%MOD;
            res=(res+MOD+1ll*f[d]*S(n/d,m/(n/d))%MOD*mu[n/d])%MOD;
        }
        return res;
    }
    int S2(int n,int m)
    {
        int res=0;
        for(int i=1;i<=m;i++)
            res=(res+f[i*n])%MOD;
        return res;
    }
    int main()
    {
        int T,n,m;
        pretype();
        scanf("%d",&T);
        while(T--){
            scanf("%d%d",&n,&m);
            printf("%d
    ",S(v[n],m));
            //cout<<S2(v[n],m)<<" "<<S2(n,m)<<endl;
        }
    }
    View Code

    卡常版(4914ms)

    #include<bits/stdc++.h>
    using namespace std;
    #define N 10000001
    #define MOD 1000000007
    int T,n,f[N],sf[N],mu[N],v[N],p[N],cnt;
    void pretype()
    {
        v[1]=f[1]=mu[1]=1;
        for(int i=2;i<N;i++){
            if(!v[i]){p[++cnt]=i,mu[i]=-1,f[i]=2,v[i]=i;}
            for(int j=1;j<=cnt && 1ll*i*p[j]<N;j++){
                v[i*p[j]]=v[i]*p[j];
                if(i%p[j]==0){f[i*p[j]]=f[i],v[i*p[j]]=v[i];break;}
                mu[i*p[j]]=-mu[i];
                f[i*p[j]]=2*f[i];
            }
        }
        for(int i=1;i<N;i++)sf[i]=(sf[i-1]+f[i])%MOD;
    }
    int S(int n,int m)
    {
        if(m==0)return 0;
        if(m==1)return f[n];
        if(n==1)return sf[m];
        int res=0;
        if(m<100 && n*m<N){
            for(int i=1;i<=m;i++)
                res=(res+f[i*n])%MOD;
            return res;
        }
        for(int d=1;d*d<=n;d++)if(n%d==0){
            res=(res+MOD+1ll*f[n/d]*S(d,m/d)%MOD*mu[d])%MOD;
            res=(res+MOD+1ll*f[d]*S(n/d,m/(n/d))%MOD*mu[n/d])%MOD;
        }
        return res;
    }
    int main()
    {
        int T,n,m;
        pretype();
        scanf("%d",&T);
        while(T--){
            scanf("%d%d",&n,&m);
            printf("%d
    ",S(v[n],m));
        }
    }
    View Code

    PS:本篇博客做法思路与[BZOJ 3512]的解法十分相似,阅读相关题解有助于进一步理解这种做法

    时间复杂度分析:

       咕咕咕.jpg,反正(O()能过())XD

  • 相关阅读:
    《MySQL必知必会》第六章:过滤数据
    《MySQL必知必会》第七章:数据过滤
    《MySQL必知必会》第五章:排序检索数据
    Java高级特性:clone()方法
    Java基础知识详解:abstract修饰符
    Java虚拟机:虚拟机内存区域和内存溢出异常
    Java虚拟机:源码到机器码
    Java虚拟机:本地方法栈与Native方法
    [LeetCode] 1481. Least Number of Unique Integers after K Removals
    [LeetCode] 331. Verify Preorder Serialization of a Binary Tree
  • 原文地址:https://www.cnblogs.com/DeaphetS/p/13525554.html
Copyright © 2011-2022 走看看