P9308 「DTOI-5」#1f1e33 题解
声明:截止 ,拿下洛谷最优解最短解,代码长度不到 1k。复杂度 。
先骂:官方题解菜!这种纯洁的数论题居然敢引入 NTT 作为标算,说明出题人不会推式子。
还有题解区一车 的题解凭啥顶那么上面,推的一坨狗屎推出来的复杂度还不优秀。
说明:下面除法默认下取整,为了方便部分分数用 代替,若无特殊说明 都表示取 。
下文记当前求的 为 , 后面设成了其他函数。
套路性的枚举 :。
注意到:。
,枚举 ,枚举 ,则 :
。
枚举 :
其中 。
此时若计算出 ,注意到显然 是积性函数。
求 时的 做个快速狄利克雷卷积即可,狄卷的复杂度是 。
考虑求 ,记 ,则可以如下线性筛求 :
考虑求 ,扣掉 ,则多算的部分为:。
于是 。
,其中 。
可以如下线性筛求 :
然后随便前缀和一下就能求出 了。
于是 都可以线性求,最后狄利克雷卷积一下做完。
留几道思考问题:
-
如何证明 是积性函数?
-
如何能推出线性筛求 的式子?
-
请尝试论证 的值能在
int
范围内存下,即线性筛的过程中不需要取模。 -
一种及其巧妙的把 转化为 的方法为:令 。
对 做两次前缀和得到 ,此时 ,请证明并尝试正向推出此做法。
#include<bits/stdc++.h>
#define LL long long
#define fr(x) freopen(#x".in","r",stdin);freopen(#x".out","w",stdout);
using namespace std;
const int N=1e6+5,mod=998244353;
int n,pr[N/10],f[N],g[N];bool v[N];
inline int md(int x){return x>=mod?x-mod:x;}
inline void init(int M)
{
f[1]=g[1]=1;
for(int i=2;i<=M;i++)
{
if(!v[i]) pr[++pr[0]]=i,f[i]=1-i,g[i]=2*i-1;
for(int j=1,p=2;j<=pr[0]&&i*p<=M;p=pr[++j])
{
v[i*p]=1;
if(i%p==0){f[i*p]=f[i];g[i*p]=p*(2*g[i]-p*g[i/p]);break;}
f[i*p]=f[i]*f[p];g[i*p]=g[i]*g[p];//注意此处不需要取模减小常数
}
}//线性筛求 f_0,F
for(int i=1;i<=M;i++) f[i]=1ll*(f[i]+mod)*i%mod,g[i]=md(g[i]-i+g[i-1]);//f 记得点乘回 i
for(int i=1;i<=M;i++) g[i]=md(g[i]+g[i-1]);
for(int i=M;i;i--) g[i]=g[i-1];g[1]=0;//巧妙方法求出 g
}
int main()
{
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);cin>>n;init(n);
for(int i=1;i<=pr[0];i++) for(int j=n/pr[i];j;j--)
for(LL k=pr[i];j*k<=n;k*=pr[i]) g[j*k]=(g[j*k]+1ll*g[j]*f[k])%mod;
//按照我给的链接材料做快速狄卷
for(int i=1;i<=n;i++) cout<<g[i]<<" ";
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】