[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} \rfloor}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} \rfloor}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} \rfloor}f(ide)$$

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

   再次注意到\(\omega\)是不同质数的乘积,因此有对任意\(T|\omega\),\(gcd(T,\frac{\omega}{T})=1\),而由于\(d|T\),故\(f(d\cdot \frac{\omega}{T})=f(d)f(\frac{\omega}{T})\),因此有$$S(\omega,m)=\sum_{T|\omega}S(T,\lfloor \frac{m}{T} \rfloor)\sum_{d|T}\mu(d)f(d)f(\frac{\omega}{T})=\sum_{T|\omega}f(\frac{\omega}{T})S(T,\lfloor \frac{m}{T} \rfloor)\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^i\cdot(-1)^i\),发现这是一个类似于二项式展开的形式,计算得出$$\sum_{d|T}\mu(d)f(d)=\sum_{i=0}^{k(T)}C_{k(T)}^{i}2^i\cdot(-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}\rfloor)$$

   得到这个式子后直接递归求解即可,最坏情况下\(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\n",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\n",S(v[n],m));
    }
}
View Code

 

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

 

时间复杂度分析:

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

posted @ 2020-08-18 20:37  DeaphetS  阅读(560)  评论(0编辑  收藏  举报