容斥,积性函数,卷积,数论

质数,欧拉函数,莫比乌斯函数等积性函数筛法

积性函数都可以在o(n)复杂度内计算出来。

如何线性筛g(x)=\sum_{d|x}f(d)

首先计算g(1)=f(1),其次当x为质数i的时候可得g(i)=g(1)+f(i).

接下来筛的时候,如果i\%prime[j]!=0g(nxt)=g(i)*g(prime[j]).

i\%prime[j]==0,我们需要手动推出g(i),g(prime[j]),g(nxt)等他们之间的关系。

//欧拉筛打表o(n)
int tot,prime[maxn];
bool isPrime[maxn];
int phi[maxn],mu[maxn];
void init()
{
    memset(isPrime,true,sizeof(isPrime));
    mu[1]=phi[1]=1,tot=0;
    for(int i=2;i<maxn;i++)
    {
        if(isPrime[i]==true)prime[++tot]=i,phi[i]=i-1,mu[i]=-1;
        for(int j=1;j<=tot;j++)
        {
            int nxt=i*prime[j];
            if(nxt>=maxn)break;
            isPrime[nxt]=false;
            if(i%prime[j]==0)
            {
                phi[nxt]=phi[i]*prime[j];
                mu[nxt]=0;
                break;
            }
            else
            {
                phi[nxt]=phi[i]*(prime[j]-1);
                mu[nxt]=-mu[i];
            }
        }
    }
}

如果我们只需要求出单个则考虑一下的筛法:

\sum_{d=1}^{n}f(\frac{n}{d})o(\sqrt{n})筛法

由于对于一连串相同的d来说,n/d的结果可能是相同的,故可以考虑同时处理一连串的值。

ll func(ll x){}
ll getans()
{
    ll ans=0;
    for(int d=1,nxt=1;d<=n;d=nxt+1)
    {
        int v=n/d;
        nxt=n/v;  //d-nxt之间的v都相同,故可以同时处理
        ans=ans+(nxt-d+1)*func(v);
    }
    return ans;
}

\sum_{d=1}^{n}g(d)f(\frac{n}{d})o(\sqrt{n})筛法

由于n/d的结果可能是相同的,因此我们可以将n/d相同结果的部分d同时处理,并将该部分dg(d)求和

ll func(ll x){}
ll getans()
{
    ll ans=0;
    for(int d=1,nxt=1;d<=n;d=nxt+1)
    {
        int v=n/d;
        nxt=n/v;  //d-nxt之间的v都相同,故可以同时处理
        ans=ans+(sumg[nxt]-sumg[d-1])*func(v);
    }
    return ans;
}

f(n)=\sum_{d=1}^{n}\frac{n}{d}的线性筛

上式等价于f(n)=\sum_{d=1}^{n}\frac{n}{d}=\sum_{d=1}^{n}\tau(d),故f(n)的线性筛可以转化为\tau(d)的线性筛再求前缀和。

 

积性函数

积性函数:对于任意互质的整数a和b有性质f(ab)=f(a)f(b)的数论函数。

完全积性函数:对于任意整数a和b有性质f(ab)=f(a)f(b)的数论函数。

\epsilon(n) = [n = 1]     卷积单位元

\varphi(n)=n\prod_{p|n}(1-1/p)  欧拉函数

1(n)=1      常数函数

Id(n)=n   线性函数 

μ(n)      莫比乌斯函数,关于非平方数的质因子数目

gcd(n,k)      最大公因子,当k固定的情况

d(n)=\sum_{d|n}1      表示n的正因子数目

σ(n)   n的所有正因子之和

\tau(n)  n的因子个数

性质1:f(x)为积性函数且g(x)为积性函数,则t(x)=f(x)*g(x)为积性函数。

性质2:f(x)为积性函数有g(x)=\sum_{d|x}f(d),则g(x)为积性函数。结合性质1,可在o(n)复杂度计算出g(x)=\sum_{d|x}f_{1}(d)*f_{2}(d)

 

\tau(n)函数的筛法

tau[1]=1;
tau[p]=2;
if(i%prime[j]==0)
{
    int times=0,tmp=i;
    while(tmp%prime[j]==0)
    {
        times++;
        tmp=tmp/prime[j];
    }
    tau[nxt]=tau[i]/(times+1)*(times+2);
}
else
{
    tau[nxt]=tau[i]*2;
}

 

狄利克雷卷积

对于两个积性函数f(x)g(x),定义它们的狄利克雷卷积为:(f*g)(n) = \sum_{d|n} f(d) g(\frac{n}{d})

数论函数与狄利克雷卷积形成群,满足结合律,封闭性,单位元,逆元交换律.两个积性函数的狄利克雷卷积还是积性函数

其中单位元为\epsilon,\epsilon(n) = [n = 1],常用积性函数见上表,当然还有其他的。

这里有比较常用的几种卷积关系:

\mu * 1=\epsilon  这说明在卷积下莫比乌斯函数和1互为逆元,故可推出下面式子:

\phi * 1=Id->\qquad \phi = Id * \mu

莫比乌斯函数

对于每个正整数n(n ≥ 2),设它的质因数分解式为:

  

  根据这个式子定义n的莫比乌斯函数为:

  

性质:

1.任意数的所有因数的莫比乌斯函数值求和为0:

 

2.莫比乌斯函数和欧拉函数的关系:

  

莫比乌斯反演形式1:因子形式:

若定义在正整数集上的两个函数,f(n)和g(n)满足对任意n有:

  f(n)=\sum_{d|n}g(d)      (1)

  则可以通过f来表示g:

  g(n)=\sum_{d|n}\mu(d)f(n/d)    (2)

  反之,亦可以由关系(2)得到(1)

形式:倍数形式:

F(n)=\sum_{n|d}f(d)=>f(n)=\sum_{n|d}\mu(d/n)F(d)

证明:在卷积的定义下,上式变得很简单,eg:

f(n)=\sum_{d|n}g(d)看成是f(n)=\sum_{d|n}(g(d)*1(n/d)) 故求g(n),直接转1的逆元,既是g(n)=\sum_{d|n}f(d)*\mu(n/d)

 

求1-n平方因子个数

如果两个数xy,且y^{2}|x^{2},简单推理可以得到y|x,则我们可以得到结论,即平方项之间的容斥关系与非平方项之间相同,故可以采用相同的容斥方法,利用非平方项的莫比乌斯函数值计算平方项,将范围缩回到\sqrt_{n}

由于n的莫比乌斯函数值表示n在容斥中系数的相反数,故F(n)=-\sum_{i=2}^{\sqrt{n}} \mu(i)*\frac{n}{i^{2}}. 非平方因子数为G(n)=\sum_{i=1}^{\sqrt{n}} \mu(i)*\frac{n}{i^{2}}

 

常见变化

1.\sum_{i=1}^{n}\sum_{j|i}f(j)

\sum_{i=1}^{n}\sum_{j|i}f(j)采用j进行遍历可得:\sum_{j=1}^{n}f(j)*(n/j),这步操作是将内层向外层进行变化,需要理解外层的含义。

也可以从数学上直接变化转成另外一种计算个数的方式\sum_{i=1}^{n}\sum_{j|i}f(j)=\sum_{j=1}^{n}\sum_{k=1}^{n/j}f(k)

 

2.[gcd(i,j)=1],该形式可以写成和式的形式,我们观察该形式,也即是当i,j互质的时候为1,其他情况为0. 莫比乌斯函数性质中就有1的所有因子的莫比乌斯值和为1,其他数因子的莫比乌斯值和为0,故可以结合得到,上式其实是查看i,j的gcd值的因子和:

[gcd(i,j)=1]= \sum_{d|gcd(i,j)} \mu(d)

求:\sum_{i=1}^{n}\sum_{j=1}^{m}[gcd(i,j)=1]\equiv \sum_{i=1}^{n}\sum_{j=1}^{m}\sum_{d|gcd(i,j)} \mu(d)\equiv \sum_{d=1}^{n}\mu(d)*(n/d)*(m/d)

后半部分可以用线性做法求d=1到n 但是观察到后面部分是个线性的除法,我们知道(n/d)(n/(d+1))可能是相同的,因为有相同的除数,故可以简化线性运算到O(\sqrt n)

方法如下:对于n来说,当前数为i,则除数为n/i,对于能使的除数为n/i的最大数也就应该等于n/(n/i),故除数为n/i的数从i到n/(n/i)都满足。这就形成了一次走多步。

注:当n=m的时候,上式变成\sum_{i=1}^{n}\sum_{j=1}^{n}[gcd(i,j)=1]\equiv 2\sum_{i=1}^{n}\phi(i)-1,参考欧拉函数的定义可知,因为是对称的!!!

 

3.[gcd(i,j)=k],同理由于i,j最小公倍数为k,故我们将整体除以k,也即是令\dot{i}=i/k,\dot{j}=j/k,故:

[gcd(\dot{i},\dot{j})=1]= \sum_{d|gcd(\dot{i},\dot{j})} \mu(d),取值范围变化成原来的1/k

 

4.求\sum_{i=1}^{n}\sum_{j=1}^{m}gcd(i,j),(n<m)

首先对gcd(i,j)分类,可以得到\sum_{i=1}^{n}\sum_{j=1}^{m}gcd(i,j)=\sum_{k=1}^{n}k\sum_{i=1}^{n}\sum_{j=1}^{m}[gcd(i,j)=k],同时除以k得到:

\sum_{k=1}^{n}k\sum_{i=1}^{n/k}\sum_{j=1}^{m/k}[gcd(i,j)=1],也即是\sum_{k=1}^{n}k\sum_{i=1}^{n}\sum_{j=1}^{m}\sum_{d|gcd(i,j)} \mu(d),可得结果:\sum_{k=1}^{n}k\sum_{d=1}^{n/k}\mu(d)*(n/k/d)*(m/k/d),等价于\sum_{k=1}^{n}k\sum_{d=1}^{n/k}\mu(d)*(n/kd)*(m/kd)复杂度降低到了O(\sqrt n*\sqrt n)=O(n)

后续可以用狄利克雷卷积进行进一步化简也可以参考4.

 

5.求\sum_{i=1}^{n}\sum_{j=1}^{m}gcd(i,j),(n<m)

我们考虑筛法,由于欧拉函数的性质:\sum_{d|n}\varphi(d)=n,我们将gcd(i,j)的值用该已有的可在线性时间筛出来的积性函数表示,即可变成\sum_{i=1}^{n}\sum_{j=1}^{m}\sum_{d|gcd(i,j)}\varphi(d),(n<m),故按照d进行统计可以化简成\sum_{d=1}^{n}\varphi(d)*(n/d)*(m/d),这就是用欧拉函数进行的反演,后续同理可以在O(\sqrt n)时间内求出来。

 

6.求\sum_{i=1}^{n}\sum_{j=1}^{m}ij(gcd(i,j)=k)

先i,j除以k得到\sum_{i=1}^{n/k}\sum_{j=1}^{m/k}ikjk(gcd(i,j)=1)=k^{2}\sum_{i=1}^{n/k}\sum_{j=1}^{m/k}ij \sum_{d|gcd(i,j)} \mu(d),按照d进行筛选,也就是说i,j都是d的倍数,故上式变成k^{2}\sum_{d=1}^{n/k}\mu(d)d^{2}\sum_{i=1}^{n/kd}i\sum_{j=1}^{m/kd}j= \frac{k^{2}}{4}\sum_{d=1}^{n/k}\mu(d)d^{2}(1+\frac{n}{kd})*(\frac{n}{kd})(1+\frac{m}{kd})*(\frac{m}{kd})

 

7.\sum_{i=1}^{n}\sum_{j=1}^{m}ijgcd(i,j)

首先容易想到按照gcd(i,j)的结果筛选\sum_{d=1}^{n}d\sum_{i=1}^{n}\sum_{j=1}^{m}ij[gcd(i,j)=d],按照上面方法处理得\sum_{d=1}^{n}d^{3}\sum_{g=1}^{n/d}\mu(g)g^{2}f{(\frac{n}{gd})}f{(\frac{m}{gd})}.

按照套路dggd的因子,令T=gd,按照T进行筛选得:\sum_{T=1}^{n}f(\frac{n}{T})f(\frac{m}{T})T^{2}\sum_{d|T}d\mu(\frac{T}{d}),后面部分是一个狄利克雷卷积,且Id*\mu=\phi,故上式变成\sum_{T=1}^{n}f(\frac{n}{T})f(\frac{m}{T})T^{2}\phi(T),当T比较小的时候可以线性筛,当T比较大的时候需要杜教筛。

 

 

 

8.求\sum_{i=1}^{n}\sum_{j=1}^{m}d(ij)

上式可以改写成\sum_{i=1}^{n}\sum_{j=1}^{m}\sum_{d|ij}1,根据 ij 之间因子个数的关系,很容易得到d(ij)=\sum_{x|i}\sum_{y|j}[gcd(x,y)=1],因此上式转化成\sum_{i=1}^{n}\sum_{j=1}^{m}\sum_{x|i}\sum_{y|j}[gcd(x,y)=1],观察一下我们可以发现,重新构造上式变成\sum_{i=1}^{n}\sum_{x|i}\sum_{j=1}^{m}\sum_{y|j}[gcd(x,y)=1],因此分别按照内层y进行筛选,再按照外层x进行筛选得到:\sum_{i=1}^{n}\sum_{x|i}\sum_{j=1}^{m}\sum_{y|j}[gcd(x,y)=1]=\sum_{x=1}^{n}(n/x)\sum_{y=1}^{m}(m/y)[gcd(x,y)=1],代入莫比乌斯函数得到\sum_{x=1}^{n}\sum_{y=1}^{m}(n/x)(m/y) \sum_{d|gcd(x,y)} \mu(d),按d筛选\sum_{i=1}^{n}\sum_{j=1}^{m}d(ij)=\sum_{d=1}^{n}\mu(d)\sum_{x=1}^{n/d}\frac{n}{xd}\sum_{y=1}^{m/d}\frac{m}{yd},预处理出后面部分即可。

 

9.求\sum_{i=1}^{a}\sum_{j=1}^{b}\sum_{k=1}^{c}d(ijk)

由7可得:

算法:遍历k,再遍历d,利用记忆化方法在sqrt(n)时间内算出后面两个部分。

CodeForces - 235E

 

 

10.\sum_{i=1}^{n}\sum_{j=1}^{m}lcm(i,j)

处理lcm是不好的选择,我们通常处理gcd,因此上式写成\sum_{i=1}^{n}\sum_{j=1}^{m}\frac{ij}{gcd(i,j)}.

上式不好变化,因此按照套路枚举gcd的结果d\sum_{d=1}^{n}\sum_{i=1}^{n/d}\sum_{j=1}^{m/d}\frac{idjd}{d}[gcd(i,j)=1]也即是\sum_{d=1}^{n}d\sum_{i=1}^{n/d}\sum_{j=1}^{m/d}ij[gcd(i,j)=1],参考前面题目做法,可得\sum_{d=1}^{n}d\sum_{g=1}^{n/d}\mu(g)g^{2}f(\frac{n}{dg})f(\frac{m}{dg})其中f(x)=\sum_{i=1}^{x}i. 即可在O(n^{2})时间处理。

观察式子由于dgdg的乘积,如果我们定义T=dg,并按照T进行筛选,由于dg都是T的因数,故对于每个T,遍历T的每个因子d,而gTd的遍历下为定值g=T/d,得到\sum_{T=1}^{n}f(\frac{n}{T})f(\frac{m}{T})\sum_{d|T}d\mu(\frac{T}{d})(\frac{T}{d})^{2}.也即是\sum_{T=1}^{n}f(\frac{n}{T})f(\frac{m}{T})T\sum_{d|T}\mu(d)d.

观察后面的式子并不是狄利克雷卷积形式,但由于f(d)=d\mu(d)都是积性函数,故t(d)=d\mu(d)也是积性函数,则g(x)=\sum_{d|x}t(d)也是积性函数。

g(T)=\sum_{d|T}\mu(d)d.我们预先对积性函数打表可在o(n)时间范围内筛选出g(T)的结果。即可变成\sum_{T=1}^{n}Tf(\frac{n}{T})f(\frac{m}{T})g(T),可以在o(\sqrt{n})时间求出。

 

11.化简类似j|i 的整除式

下标合并

\sum_{i=1}^{n}\sum_{j|i} f(i) = \sum_{i=1}^{n}\sum_{\frac{j}{j}|\frac{i}{j}} f(\frac{i}{j}*j) = \sum_{\frac{i}{j}=1}^{n} f(\frac{i}{j}*j)=(\frac{i}{j}\rightarrow i) \sum_{i=1}^{n} f(ij)

杜教筛(积性函数前缀和)

S(n) = \sum_{i=1}^{n} f(i),要求S(n),其中f(i)是一个积性函数。

考虑以下式子\sum_{i=1}^{n} (f*g)(i),利用积性函数的定义上式变成\sum_{i=1}^{n} \sum_{d|i} g(d)f(\frac{i}{d}),按照d进行筛选可得式子:\sum_{d=1}^{n}(g(d)* \sum_{d|i}f(\frac{i}{d}))

观察后面一个式子,可以知道令j=i/d,上式化简成\sum_{d=1}^{n}(g(d)* \sum_{j=1}^{n/d}f(j)),式子右部分其实就是s(n/d),故上式的结果是:

\sum_{i=1}^{n} (f*g)(i)=\sum_{i=1}^{n}g(i)* s(n/i)=g(1)*s(n)+\sum_{i=2}^{n}g(i)* s(n/i),式子中出现了s(n),提出该项有:

g(1)*s(n)=\sum_{i=1}^{n} (f*g)(i)-\sum_{i=2}^{n}g(i)* s(n/i)这个式子就能够很快的得出结果。我们只需要找到合适的g函数(g(n)=1)就行。

如果我们已知g(x),则上式中f*g(x)=\sum_{d|x}f(d)g(\frac{x}{d}),如果根据f的表达式凑上已知的g(x),能方便的求出f*g(x),则上式中只有s(n/i)未知,该处我们可以采用递归来写,递归的复杂度只有o(\sqrt{n}),采用map写映射

故,为了求如下形式的结果ans=\sum_{T=1}^{n}func(\frac{n}{T})f(T),有:


ll func(ll x){}
ll sumf[maxn];
ll getans(ll n)
{
    ll ans=0;
    for(int d=1,nxt=1,;d<=n;d=nxt+1)
    {
        int v=n/d;
        nxt=n/v;  //d-nxt之间的v都相同,故可以同时处理
        ans=ans+(sumf[nxt]-sumf[d-1])*func(v);
    }
    return ans;
}

而如果说n过大,我们无法在线性时间内求出来所有的fsumf函数,我们就需要用杜教筛令sumf(n) = \sum_{i=1}^{n} f(i),得到g(1)*sumf(n)=\sum_{i=1}^{n} (f*g)(i)-\sum_{i=2}^{n}g(i)* sumf(n/i),人工令g(x),并手动求出f*g(i)的已知函数fg(i),则sumf(n)=\frac{\sum_{i=1}^{n} (f*g)(i)-\sum_{i=2}^{n}g(i)* sumf(n/i)}{g(1)}.

由于递归寻找sumf(n/i)可能比较消耗时间,我们可以对前面较小的数线性筛出sumf结果。故代码如下:

int tot,prime[maxn];
bool isPrime[maxn];
int phi[maxn],mu[maxn];
ll sumf[maxn];
void init()
{
    memset(isPrime,true,sizeof(isPrime));
    mu[1]=phi[1]=1,tot=0;
    for(int i=2;i<maxn;i++)
    {
        if(isPrime[i]==true)prime[++tot]=i,phi[i]=i-1,mu[i]=-1;
        for(int j=1;j<=tot;j++)
        {
            int nxt=i*prime[j];
            if(nxt>=maxn)break;
            isPrime[nxt]=false;
            if(i%prime[j]==0)
            {
                phi[nxt]=phi[i]*prime[j];
                mu[nxt]=0;
                break;
            }
            else
            {
                phi[nxt]=phi[i]*(prime[j]-1);
                mu[nxt]=-mu[i];
            }
        }
    }
    //for(int i=1;i<maxn;i++)sumf[i]=sumf[i-1]+phi[i];
}

const int hashsize=31643,maxnode=100000;
struct Hash
{
    struct HashNode{ll key;ll val;int next;};
    HashNode node[maxnode];
    int head[maxnode],tot;
    Hash(){clear();}
    void clear(){memset(head,-1,sizeof(head));tot=0;}
    ll add(ll key,ll val)
    {
        ll x=key%hashsize;
        node[tot].key=key,node[tot].val=val;
        node[tot].next=head[x];
        head[x]=tot++;
        return val;
    }
    ll operator[](ll key)
    {
        ll x=key%hashsize;
        for(int i=head[x];i!=-1;i=node[i].next)
            if(node[i].key==key) return node[i].val;
        return -1;
    }
};
Hash hashmap;

inline ll g(ll x){}
inline ll func(ll x){}
inline ll sumg(ll n){}
ll sumfg(ll n){}  // 1-n的fg卷积求和
ll Sumf(ll n) //杜教筛求1-n的f函数和
{
    if(n<maxn)return sumf[n];
    if(hashmap[n]!=-1)return hashmap[n];
    ll ans=sumfg(n);
    for(ll d=2,nxt=2;d<=n;d=nxt+1)
    {
        ll v=n/d;
        nxt=n/v;
        ll sumgnd=(sumg(nxt)-sumg(d-1))%mod;
        ans=(ans-sumgnd*Sumf(v))%mod;
        ans=(ans+mod)%mod;
    }
    ans=ans/g(1);     //需要的话记得对g(1)求逆元
    return hashmap.add(n,ans);
}
ll getans()
{
    ll ans=0;
    for(ll d=1,nxt=1;d<=n;d=nxt+1)
    {
        ll v=n/d;
        nxt=n/v;  //d-nxt之间的v都相同,故可以同时处理
        ll sumnd=(Sumf(nxt)-Sumf(d-1))%mod;  //函数求和
        ll funcv=func(v)*func(v)%mod;       //相同v的f函数值
        ans=(ans+sumnd*funcv)%mod;
        ans=(ans+mod)%mod;
    }
    return ans;
}

 

Min25筛

 

 

低于线性时间积性函数前缀和

部分转化公式

1.\sum_{i = 1}^{n} \sum_{j|i} j = \sum_{i = 1}^{n} i * \lfloor \frac{n}{i} \rfloor = \sum_{i = 1}^{n} \frac{\lfloor \frac{n}{i} \rfloor * ( 1 + \lfloor \frac{n}{i} \rfloor)}{2}

2.\sum_{i|n} \phi(i) = n, \phi(n) = n - \sum_{i|n, i < n}

3.[n = 1] = \sum_{i|n} \mu(i)

4.\sum_{i=1}^{n}\sum_{j|i} f(j) = \sum_{i=1}^{n}\sum_{j=1}^{\lfloor\frac{n}{i}\rfloor}f(j)

5.\mu^2(i) = \sum_{j^2|i}\mu(j)

6.[gcd(i, j)] = \sum_{d|i, d|j}\mu(d)

例题:51NOD 欧拉函数之和    51NOD 莫比乌斯函数之和 

一般化: 
对于数论函数f(i), 想要求解 S(n) = \sum_{i=1}^{n} f(i),那么找到一个合适的数论函数g(n)g(n),可以得到递推式: 
g(1)S(n) = \sum_{i=1}^{n}(f*g)(i) - \sum_{i=2}^{n}g(i)S(\lfloor\frac{n}{i}\rfloor)
如果f(i)是积性,那么可以O(n^{2/3}),否则暴力递归O(n^{3/4})。 
可以发现取g(n) = 1即可得到上述欧拉函数与莫比乌斯函数前缀和的递推公式。

提供几个优秀的学习来源: 
tls的博客:https://blog.csdn.net/skywalkert/article/details/50500009 
吉利爷的博客:http://jiruyi910387714.is-programmer.com/posts/195270.html 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值