线性筛,积性函数,狄利克雷卷积,常见积性函数的筛法

一些性质

  • 积性函数:对于函数 f(n) ,若满足对任意互质的数字 a,bab=n f(n)=f(a)f(b) ,那么称函数f为积性函数。
  • 狄利克雷卷积:对于函数f,g,定义它们的卷积为
    (fg)(n)=d|nf(d)g(nd)
    狄利克雷卷积满足很多性质:
    交换律: fg=gf
    结合律: (fg)h=f(gh)
  • 两个积性函数的狄利克雷卷积仍为积性函数。
  • 积性函数都可以用线性筛筛出来

    怎么筛?
    f(Pk)P


    如果你一眼就看出来了那就只能Orz了

    几道题

    Bzoj4804: 欧拉心算
    BZOJ2693jzptab
    BZOJ4407 :于神之怒加强版

一些常见积性函数的筛法

莫比乌斯函数 μ(n)

这个比较简单, μ(1)=1 i 为质数时μ(i)=1,最小质因子筛到它的时候正负号反过来,否则为 0


    isprime[1] = 1; mu[1] = 1;
    for(int i = 2; i < N; ++i){
        if(!isprime[i]){  prime[++num] = i; mu[i] = -1;  }
        for(int j = 1; j <= num && i * prime[j] < N; ++j){
            isprime[i * prime[j]] = 1;
            if(i % prime[j]) mu[i * prime[j]] = -mu[i];
            else{  mu[i * prime[j]] = 0; break;  }
        }
    }

乘法逆元inv(i)

求一个数在模p意义下的逆元
p=ix+j ,则 ix+j0(mod p)
同时除以 ijxj1+i10(mod p)
移项 i1xj1(mod p)
x=p div ij=p mod i
所以 inv(i)=inv(p%i)(p/i)
不用筛了,递推就可以了

inv[1] = 1; for(int i = 2; i < p; ++i) inv[i] = -(p / i) * inv[p % i] % p + p) % p;

补充阶乘逆元的递推,求出 inv(n) inv(i)=inv(i+1)(i+1) 倒着来就行了

fac[0] = inv[0] = 1;
for(int i = 1; i <= n; ++i) fac[i] = fac[i] * i % p;
inv[n] = Getinv(fac[n]); //Exgcd or Fermat
for(int i = 1; i < n; i++) inv[i] = inv[i + 1] * (i + 1) % p;

欧拉函数 φ(n)

公式:
npn=Πpaii
φ(n)=nΠ(11pi)

那么 φ(1)=1 nφ(n)=n1 ,最小质因子筛到它时乘上质因子 p1 ,否则乘上这个质数

isprime[1] = 1; phi[1] = 1;
    for(int i = 2; i < N; ++i){
        if(!isprime[i]){  prime[++num] = i; phi[i] = i - 1;  }
        for(int j = 1; j <= num && i * prime[j] < N; ++j){
            isprime[i * prime[j]] = 1;
            if(i % prime[j]) phi[i * prime[j]] = phi[i] * (prime[j] - 1);
            else{  phi[i * prime[j]] = phi[i] * prime[j]; break;  }
        }
    }

约数个数 d(n)

公式:
还是分解
d(n)=Π(ai+1)

我们记录下每个数最小质因子的指数记为 pred 就好了

isprime[1] = 1; d[1] = 1;
    for(int i = 2; i < N; ++i){
        if(!isprime[i]){  prime[++num] = i; d[i] = 2; pred[i] = 1;  }
        for(int j = 1; j <= num && i * prime[j] < N; ++j){
            isprime[i * prime[j]] = 1;
            if(i % prime[j]) d[i * prime[j]] = d[i] * d[prime[j]], pred[i * prime[j]] = 1;
            else{  pred[i * prime[j]] = pred[i] + 1; d[i * prime[j]] = d[i] / (pred[i] + 1) * (pred[i] + 2); break;  }
        }
    }

约数的和 σ(n)

公式:
又是分解
σ(n)=Π(aij=0pji)

这个就很烦了。。。

也可以筛,开两个个数组,一个 powd 记录每个数最小质因子的指数次幂,另一个 sumd 记录每个数最小质因子 ai=0pi 就可以了

我们把这个鬼里鬼气的 σf

IL void Prepare(){
    isprime[1] = 1; f[1] = mu[1] = 1;
    for(int i = 2; i < N; ++i){
        if(!isprime[i]){
            prime[++num] = i; f[i] = i + 1; mu[i] = -1;
            sumd[i] = 1 + i; powd[i] = i;
        }
        for(int j = 1; j <= num && i * prime[j] < N; ++j){
            isprime[i * prime[j]] = 1;
            if(i % prime[j]){
                sumd[i * prime[j]] = 1 + prime[j]; powd[i * prime[j]] = prime[j];
                f[i * prime[j]] = f[i] * f[prime[j]];
            }
            else{
                powd[i * prime[j]] = powd[i] * prime[j];
                sumd[i * prime[j]] = sumd[i] + powd[i * prime[j]];
                f[i * prime[j]] = f[i] / sumd[i] * sumd[i * prime[j]];
                break;
            }
        }
    }
}

完结

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值