狄利克雷生成函数学习笔记
首先定义狄利克雷生成函数:
定义常函数即黎曼函数
考虑乘法的定义就是狄利克雷卷积:
复杂度 。
除法的情况考虑
首先求出 ,然后对于其余位置有:
复杂度 。
考虑上述两个运算并没有用到生成函数的性质。一般来说,生成函数的一大重要性质就是 和 。
考虑
重点在于 。
而
所以重点就在于 ,而 在模意义下并没有封闭形式。
考虑没有封闭形式时我们通常用扩域的方式处理:设 表示第 个质数,令 。显然对于任意数字 ,。
考虑到由于任何运算都是在因数之间进行的,故任意时刻 的指数一定是正的。故把扩域后的数字当成多元生成函数,就可以直接定义乘法除法。换句话说所有操作的指数部分都是整除的状态下进行的。类似于 。令 表示 所有系数之和。这种情况下一定有 。
注意到 其实本身是没有定义的,所以我们可以大胆猜测最后的结果中一定不带 ,故最后 就是答案。
故我们直接记录系数之和。而对于一个自然数 , 就是其所有因数之和。直接令 即可。
故
考虑 ,有 ,因为 相当于每一项乘了 ,可以用类似除法的方式:
Dirichlet -th root
快速幂就直接套用 和 即可。
复杂度 。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#define N 1000010
#define mod 998244353
#define S(a) ((int)a.size())
#define R(a) a.resize(n+1)
#define il inline
using namespace std;
il int add(int x,int y){return x+y>=mod?x+y-mod:x+y;}
il int dec(int x,int y){return x-y<0?x-y+mod:x-y;}
typedef vector<int> poly;
int ksm(int a,int b=mod-2)
{
int r=1;
for(;b;b>>=1,a=1ll*a*a%mod) if(b&1) r=1ll*r*a%mod;
return r;
}
int n,c[N],ci[N];
void init()
{
c[1]=0;
for(int i=2;i<=n;i++)
{
if(!c[i]) c[i]=1;
ci[i]=ksm(c[i]);
for(int j=2;i*j<=n;j++) c[i*j]=c[i]+c[j];
}
}
poly operator +(poly a,poly b)
{
R(a);R(b);
poly c(n+1);
for(int i=1;i<=n;i++) c[i]=add(a[i],b[i]);
return c;
}
poly operator -(poly a,poly b)
{
R(a);R(b);
poly c(n+1);
for(int i=1;i<=n;i++) c[i]=dec(a[i],b[i]);
return c;
}
poly operator *(poly a,int b){for(int i=0;i<S(a);i++) a[i]=1ll*a[i]*b%mod;return a;}
poly operator *(poly a,poly b)
{
R(a);R(b);
poly f(n+1);
for(int i=1;i<=n;i++)
for(int j=1;i*j<=n;j++) f[i*j]=(f[i*j]+1ll*a[i]*b[j])%mod;
return f;
}
poly operator /(poly a,poly b)
{
R(a);R(b);
poly f(n+1);
for(int i=1,r=ksm(b[1]);i<=n;i++) f[i]=1ll*a[i]*r%mod;
for(int i=1;i<=n;i++)
for(int j=2;i*j<=n;j++) f[i*j]=dec(f[i*j],1ll*f[i]*b[j]%mod)%mod;
return f;
}
poly Der(poly a){for(int i=1;i<=n;i++) a[i]=1ll*a[i]*c[i]%mod;return a;}
poly Int(poly a){for(int i=1;i<=n;i++) a[i]=1ll*a[i]*ci[i]%mod;return a;}
poly ln(poly a){R(a);return Int(Der(a)/a);}
poly exp(poly a)
{
poly f=Der(a),d=f;
d[1]=1;
for(int i=2;i<=n;i++)
{
d[i]=1ll*d[i]*ci[i]%mod;
for(int j=2;i*j<=n;j++) d[i*j]=add(d[i*j],1ll*f[j]*d[i]%mod)%mod;
}
return d;
}
poly ksm(poly a,int k){return exp(ln(a)*k);}
poly f;
int main()
{
int k;
scanf("%d%d",&n,&k);
init();
R(f);
for(int i=1;i<=n;i++) scanf("%d",&f[i]);
f=ksm(f,ksm(k));
for(int i=1;i<=n;i++) printf("%d ",f[i]);
return 0;
}
本文来自博客园,作者:Flying2018,转载请注明原文链接:https://www.cnblogs.com/Flying2018/p/14675822.html
分类:
算法:数学相关 > 数论
, 算法:数学相关 > 生成函数
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理