卷积的求法与配线性筛

本文探讨了如何利用反演公式和容斥原理求解卷积问题,通过正向和反向推导,介绍了两种方法:直接容斥和利用莫比乌斯函数的反演,分别适用于不同场景。实例演示了从F(x)到f(x)的转换,以及如何在积性函数中实现线性筛优化。
摘要由CSDN通过智能技术生成

如果是已知F(x),求f(x)的话,(就是f(x)是被卷函数,F(x)是卷积)
可以利用容斥的方法,也可以利用反演公式,利用反演公式一般起到两个作用,化简方便降低复杂度或者是配出积性函数来线性筛。
直接容斥求的原理如下,复杂度为nlogn .但前提是F(x)已知且易求得。
在这里插入图片描述
代码如下
这里是正向推。

for (int i=1;i<=MAX;++i)
	for (int j=i<<1;j<=MAX;j+=i)
		F[j]-=F[i];

或者是利用反演,乘上一个莫比乌斯函数,这样就可以只考虑素数,并且枚举素数的时候,可以很容易的得到递推关系。

 for (int i = 1; i <= N; i++) f[i] = F[i];
  for (int i = 0; i < prime_count; i++)
    for (int j = N / prime[i]; j >= 1; j--)
      f[j * prime[i]] = f[j * prime[i]] - f[j]

举个例子
在这里插入图片描述
1 筛完F,直接暴力来。nlogn
for (int i=1;i<=MAX;++i)
for (int j=i<<1;j<=MAX;j+=i)
F[j]-=F[i];

2
nloglogn
for (int i = 1; i <= N; i++) f[i] = F[i];
for (int i = 0; i < prime_count; i++)
for (int j = N / prime[i]; j >= 1; j–)
f[j * prime[i]] = f[j * prime[i]] - f[j]
或者是筛出mu,再
for(int i=1;i<=cnt;i++)
{
for(int j=1;j<=nn/prime[i];j++)
{
f[prime[i]*j]+=mu[j];
}
}

然后这个可以线性筛,但是这不是积性函数,所以,能线性筛的不一定非得是积性函数。
如下
在这里插入图片描述

mu[1]=1;
 g[1]=1;
    for(int i=2;i<N;++i) {
        if(g[i]==0) p[++tot]=i,mu[i]=-1,f[i]=1;
        for(int j=1;j<=tot&&p[j]<N/i;++j) {
           g[i*p[j]]=1;
            if(i%p[j]==0) {
                f[x]=mu[i];
                mu[x]=0;
                break;
            } else {
                f[x]=-f[i]+mu[i];
                mu[x]=-mu[i];
            }
        }
        f[i]+=f[i-1];
    }
   

若是f(x)已知,求F(x),枚举倍数就好。复杂度nlogn,但是如果f(x) 是积性函数,线性筛复杂度o(n)。这里本就不需要反演,所以没法乘上mu,也就没法枚举素数。
举个例子
f ( n ) f(n) f(n)= ∑ d ∣ N \sum_{d|N} dNd φ ( n / d ) \varphi(n/d) φ(n/d)
1
for(int i=1;i<=nn;i++)
{
for(int j=i;j<=nn;j=j+i)
{
f[j]+=phi[i]*(j/i);
}
}

2

for(int i=1;i<=nn;i++)
    f[i]=phi[i];
    for(int i=2;i<=nn/i;i++)
    {
        f[i*i]+=phi[i]*i;
        for(int j=i+1;j<=nn/i;j++)
        {
            f[i*j]+=phi[i]*j+phi[j]*i;//+= 可能写成=出错
        }
    }

然后也可以线性筛。
线性筛在弄得过程中要找到这样几个关系,
f ( 1 ) f(1) f(1), f ( p ) f( p) f(p) , , f ( p k ) f( p ^ k) f(pk) f ( p k − 1 ) f(p^{k-1}) f(pk1)的关系。
如下。
在这里插入图片描述

g[1]=1;
   phi[1]=1;
   f[1]=1;//这里f[1]与phi[1]一样,f[1]在下面也筛不到
   for(int i=2;i<=nn;i++)
   {
       if(g[i]==0)
       {
           prime[++cnt]=i;
           phi[i]=i-1;
           r[i]=i;
           f[i]=i*2-1;
       }
       for(int j=1;j<=cnt&&prime[j]<=nn/i;j++)
       {
           g[i*prime[j]]=1;
           if(i%prime[j]==0)
           {
               r[i*prime[j]]=prime[j]*r[i];
               phi[i*prime[j]]=prime[j]*phi[i];
               if(r[i]==i)
               {
                   f[i*prime[j]]=f[i]*prime[j]+phi[i*prime[j]];
               }
               else 
               {
                   f[i*prime[j]]=f[i/r[i]]*f[prime[j]*r[i]];
               }
               break;
           }
           r[i*prime[j]]=prime[j];
           phi[i*prime[j]]=phi[prime[j]]*phi[i];
           f[i*prime[j]]=f[i]*f[prime[j]];
       }
   }

再举几个例子。
在这里插入图片描述

g[1]=1;f[1]=1;
	for(int i=2;i<=MAX;++i)
	{
		if(!g[i])pri[++tot]=i,f[i]=(i-1ll*i*i%MOD+MOD)%MOD;
		for(int j=1;j<=tot&&i*pri[j]<=MAX;++j)
		{
			g[i*pri[j]]=1;
			if(i%pri[j]==0){f[i*pri[j]]=1ll*f[i]*pri[j]%MOD;break;}
			else f[i*pri[j]]=1ll*f[i]*f[pri[j]]%MOD;
		}
	}

待续。

几个枚举倍数的东西。
求约数和,不包括本身,求约数个数则+1 。

void work(int n) {
    for (int i = 1; i <= n/i; i++) {
        for (int j = i*2; j <= n; j+=i)p[j] += i;
    }
}

求约数和,包括本身

void work(int n) {
    for (int i = 1; i <= n; i++) {
        for (int j = i; j <= n; j+=i)p[j] += i;
    }
}

求欧拉函数

phi[1]=1;
 for(int i=2;i<=nn;i++)
    {
       if(phi[i]==0)
       {
           for(int j=i;j<=nn;j=j+i)
           {
               if(phi[j]==0)
               phi[j]=j;
               phi[j]=phi[j]-phi[j]/i;
           }
       }
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值