说一下积性函数咯(更新中).

说一下积性函数咯.

积性函数

积性函数(Multiplicative function)就是满足 f(1)=1 f(ab)=f(a)f(b) 的函数,而且它在数论中也起到比较大的作用.. 常见的有欧拉函数 ϕ 、默比乌斯函数 μ 、约数个数 σ 、只有1才等于1的函数 ϵ ..


欧拉函数

欧拉函数是积性函数中算是比较多用的函数了, ϕ(n) 表示与 n 互质且比n小的数的个数,例如: ϕ(8)=4 ϕ(64)=16 ϕ(100)=40 ..
欧拉函数有以下几个性质:

ϕ(pk)=(p1)pk1

n=pr11pr22pr33prkk ,则
ϕ(n)=1ikϕ(prii)
ϕ(n)=1ikpri1i(pi1)
ϕ(n)=np|n(11p)


欧拉定理

对于正整数 a n,若 (a,n)=1 ,那么 aϕ(n)1(modn)

下面有一个简单的推论:

(a,n)=1 的情况下,如果有 axay(modn) ,那么定有 xy(modϕ(n))


积性函数的一些应用

说在前面

有一些公式是比较常用的,下面给出:

n=d|nϕ(d)

ϵ(n)=d|nμ(d) (莫比乌斯反演)

1ind1=nd

1in1jmij=(1+n)n(1+m)m4

虽然这些公式都非常简单,但是能真正在做题的时候用到并不是一件容易的事,希望所有来看这篇博文的人都注意一下

例题1:[bzoj1257]CQOI2007余数之和

题意:求 1ink mod i (n109)

1ink mod i
=1inkii
=nk1inkii

由于 ki 对于某些不同的 i 是会相同的,而且对于从1 n 是一个非递增的序列,所以我们可以用分块来解决

如果 i 是当前这一块最靠左的端点,那么j=kki便是这一块最靠右的端点

这样的分块计算答案的时间复杂度为 O(n)

code

/**************************************************************
    Problem: 1257
    Language: C++
    Result: Accepted
    Time:8 ms
    Memory:804 kb
****************************************************************/

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#define LL long long
using namespace std;
LL n, K;
int main (){
    LL i, j, k;
    scanf ( "%lld%lld", &n, &K );
    LL sum = 0;
    i = 1;
    while ( i <= n ){
        k = K/i;
        if ( k == 0 ) j = n;
        else j = K/k;
        if ( j > n ) j = n;
        sum += (i+j)*(j-i+1)/2*k;
        i = j+1;
    }
    printf ( "%lld\n", K*n-sum );
    return 0;
}

例题2:[bzoj2705]SDOI2012longge

题意:求 1in(i,n) (n109)

枚举 d=(i,n) ,设 i=id

1in(i,n)
=d|nidn,(id,n)=dd
=d|ndind,(i,nd)=11

由于 ind,(i,nd)=11 便是求与 nd 互质且比它小的数的个数,所以

=d|ndϕ(nd)

那么我们只需要枚举d即可,时间复杂度 O(n)

code

/**************************************************************
    Problem: 2705
    Language: C++
    Result: Accepted
    Time:16 ms
    Memory:1300 kb
****************************************************************/

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <cmath>
#include <vector>
#define LL long long
using namespace std;
LL n;
LL nu[31000], sum[31000], tot, ans;
void dfs ( LL x, LL phi, LL num ){
    if ( x == tot+1 ) ans += n/num*phi;
    else {
        LL pf = 1;
        dfs ( x+1, phi, num );
        for ( LL i = 1; i <= sum[x]; i ++ ){
            dfs ( x+1, phi*pf*(nu[x]-1), num*pf*nu[x] );
            pf *= nu[x];
        }
    }
}
int main (){
    LL i, j, k;
    scanf ( "%lld", &n );
    tot = 0;
    LL x = n;
    for ( i = 2; i <= sqrt(n); i ++ ){
        if ( x % i == 0 ){
            tot ++;
            nu[tot] = i;
            while ( x % i == 0 ){ sum[tot] ++; x /= i; }
        }
    }
    tot ++;
    nu[tot] = x; sum[tot] = 1;
    ans = 0;
    dfs ( 1, 1, 1 );
    printf ( "%lld\n", ans );
    return 0;
}

例题3:[bzoj2226]SPOJ LCMSUM

题意:求 1in[i,n] (n107)

枚举 d (i,n)=d,设 i=id
1in[i,n]
=d|nind,(id,n)=didnd
=d|nind,(i,nd)=1in
=nd|nind,(i,nd)=1i
这里要用到这个公式:
dn,(d,n)=1d=ϕ(d)d2
但是当 n=1 的时候并不满足上述公式,少了 12 ,所以我们可以把它加上(反正其他的 n 加上12也不会有什么变化)
  原式 =n(12+d|nϕ(nd)nd2)

=n(1+d|nϕ(nd)nd)2

我们可以在线性时间内求出 ϕ(nd) 的值,同时我们也可以在 O(nlogn) 的时间内求出 d|nϕ(nd)nd 的值(有 O(n) 的方法,但是我好像不会0.0)
所以这道题就变成了一个 O(nlogn) 预处理、 O(1) 询问的问题

code

/**************************************************************
    Problem: 2226
    Language: C++
    Result: Accepted
    Time:7308 ms
    Memory:27660 kb
****************************************************************/

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <cmath>
#define LL long long
using namespace std;
const int Maxn = 1100000;
LL n;
LL phi[Maxn], prime[Maxn], pr;
LL ans[Maxn];
bool v[Maxn];
void ss (){
    memset ( v, true, sizeof (v) );
    int i, j;
    phi[1] = 1;
    for ( i = 2; i <= 1000000; i ++ ){
        if ( v[i] == true ){
            prime[++pr] = i;
            phi[i] = i-1;
        }
        for ( j = 1; j <= pr && i*prime[j] <= 1000000; j ++ ){
            v[i*prime[j]] = false;
            if ( i % prime[j] == 0 ){
                phi[i*prime[j]] = phi[i] * prime[j];
                break;
            }
            else phi[i*prime[j]] = phi[i] * phi[prime[j]];
        }
    }
    for ( i = 1; i <= 1000000; i ++ ){
        for ( j = i; j <= 1000000; j += i ){
            ans[j] += i*phi[i];
        }
    }
}
int main (){
    LL i, j, k;
    LL T;
    scanf ( "%lld", &T );
    ss ();
    while ( T -- ){
        scanf ( "%lld", &n );
        printf ( "%lld\n", (ans[n]+1)*n/2 );
    }
    return 0;
}

例题4:[bzoj2005][Noi2010]能量采集

题意:求 1in1jm2((i,j)1)+1 (1n,m105)

我们可以先简化公式

1in1jm2((i,j)1)+1
=21in1jm(i,j)nm

那么问题就简化为求 1in1jm(i,j)

1in1jm(i,j)
=1in1jmd|(i,j)ϕ(d)
=dϕ(d)indjmd1
=dϕ(d)ndmd

由于 ndmd 在某一段的 d 会保持不变,所以我们可以用分块来解决,而ϕ(d)我们可以在线性时间内筛出预处理即可

code

/**************************************************************
    Problem: 2005
    Language: C++
    Result: Accepted
    Time:80 ms
    Memory:2632 kb
****************************************************************/

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <cmath>
#define LL long long
using namespace std;
const LL Maxn = 110000;
LL n, m;
LL phi[Maxn], prime[Maxn], pr;
bool v[Maxn];
LL _min ( LL x, LL y ){ return x < y ? x : y; }
void ss (){
    phi[1] = 1;
    LL i, j;
    for ( i = 2; i <= 100000; i ++ ){
        if ( v[i] == false ){
            prime[++pr] = i;
            phi[i] = i-1;
        }
        for ( j = 1; j <= pr && i*prime[j] <= 100000; j ++ ){
            v[i*prime[j]] = true;
            if ( i % prime[j] == 0 ){
                phi[i*prime[j]] = phi[i]*prime[j];
                break;
            }
            else phi[i*prime[j]] = phi[i]*phi[prime[j]];
        }
    }
    for ( i = 2; i <= 100000; i ++ ) phi[i] += phi[i-1];
}
int main (){
    LL d, i, j, k;
    scanf ( "%lld%lld", &n, &m );
    if ( n < m ) swap ( n, m );
    ss ();
    d = 1;
    LL ans = 0;
    while ( d <= m ){
        LL kn = n/d, km = m/d;
        LL jn, jm;
        if ( kn == 0 ) jn = n;
        else jn = n/kn;
        if ( jn > n ) jn = n;
        if ( km == 0 ) jm = m;
        else jm = m/km;
        if ( jm > m ) jm = m;
        j = _min ( jn, jm );
        ans += (phi[j]-phi[d-1])*kn*km;
        d = j+1;
    }
    printf ( "%lld\n", ans*2-n*m );
    return 0;
}

例题5:[bzoj1101][POI2007]Zap

题意:求 1in1jm[(i,j)=d](1dn,m5×104)

i=id j=jd
1in1jm[(i,j)=d]
=indjmd[(i,j)=1]
=indjmdϵ(i,j)
来一波莫比乌斯反演:
ϵ(n)=d|nμ(d)
所以
=indjmdk|(i,j)μ(k)

继续设 i=i′′k j=j′′k
那么我们又可以继续推到

=kμ(k)i′′ndkj′′mdk1
这又回到了一个比较经典的问题:
=kμ(k)ndkmdk

我们可以看出 μ(k) 可以在线性时间内筛出,然后剩下的可以用分块解决0.0

code

/**************************************************************
    Problem: 1101
    Language: C++
    Result: Accepted
    Time:11132 ms
    Memory:1652 kb
****************************************************************/

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#define LL long long
using namespace std;
const LL Maxn = 51000;
LL n, m;
LL mu[Maxn], prime[Maxn], pr;
bool v[Maxn];
LL _min ( LL x, LL y ){ return x < y ? x : y; }
void get_mu (){
    LL i, j, k;
    pr = 0;
    mu[1] = 1;
    memset ( v, false, sizeof (v) );
    for ( i = 2; i <= 50000; i ++ ){
        if ( v[i] == false ){
            prime[++pr] = i;
            mu[i] = -1;
        }
        for ( j = 1; j <= pr && i*prime[j] <= 50000; j ++ ){
            v[i*prime[j]] = true;
            if ( i % prime[j] == 0 ){
                mu[i*prime[j]] = 0;
            }
            else mu[i*prime[j]] = -mu[i];
        }
    }
    for ( i = 2; i <= 50000; i ++ ) mu[i] += mu[i-1];
}
int main (){
    LL i, j, k, T, d, ans;
    scanf ( "%lld", &T );
    get_mu ();
    while ( T -- ){
        scanf ( "%lld%lld%lld", &n, &m, &d );
        if ( n < m ) swap ( n, m );
        n /= d; m /= d;
        i = 1; ans = 0;
        while ( i <= m ){
            LL nk = n/(n/i), mk = m/(m/i);
            j = _min ( nk, mk );
            ans += (n/i)*(m/i)*(mu[j]-mu[i-1]);
            i = j+1;
        }
        printf ( "%lld\n", ans );
    }
    return 0;
}

例题6:[bzoj2154] Crash的数字表格

题意:求 1in1jm[i,j] (n107)
1in1jm[i,j]
=1in1jmij(i,j)

枚举 (i,j)=d ,设 i=id j=jd

=dindjmdidjdd[(i,j)=1]
=ddindjmdij×ϵ(i,j)
再来一波莫比乌斯反演

=ddindjmdijk|(i,j)μ(k)
那么我们再枚举 k ,设i′′=ik j′′=jk

=ddkμ(k)i′′ndkj′′mdki′′kj′′k
=ddkμ(k)k2i′′ndkj′′mdki′′j′′
=14ddkμ(k)k2(1+ndk)ndk(1+mdk)mdk

那么上式既枚举了 d ,也枚举了k,而且 d k没有必然的联系,这样的式子是不能在线性时间内实现的
所以我们设 D=dk ,通过枚举 D 来解决我们的问题
=14Dd|Ddμ(Dd)(Dd)2(1+nD)nD(1+mD)mD
进一步化简该式子
=14D(1+nD)nD(1+mD)mDd|Dμ(Dd)D2d

这个式子的后半段是可以在线性时间筛出(其实我不太懂,是翁神教我的),然后前半段又可以通过分块来解决,所以该做法的时间复杂度为 O(n)O(n)


最后

先写这么多吧,更难的以后再补了

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值