P9308 「DTOI-5」#1f1e33 题解

声明:截止 2024.8.1,拿下洛谷最优解最短解,代码长度不到 1k。复杂度 O(nloglogn)

先骂:官方题解菜!这种纯洁的数论题居然敢引入 NTT 作为标算,说明出题人不会推式子。

还有题解区一车 log 的题解凭啥顶那么上面,推的一坨狗屎推出来的复杂度还不优秀。


说明:下面除法默认下取整,为了方便部分分数用 / 代替,若无特殊说明 (i,j) 都表示取 gcd

下文记当前求的 f(n)ansf 后面设成了其他函数。

套路性的枚举 gcdans=d=1n1j,k,j+kn/d[(j,k)=1]lcm(nd(j+k),d)

注意到:lcm(nd(j+k),d)=d(nd(j+k))gcd(nd(j+k),d)=d(d,n)(nd(j+k))

ans=d=1nd(d,n)1j,k,j+kn/d[(j,k)=1](nd(j+k)),枚举 s=j+k,枚举 j,则 (j,k)=(s,j)

ans=d=1nd(d,n)s=2n/d(nds)j=1s1[(s,j)=1]=d=1nd(d,n)s=2n/d(nds)φ(s)=i=1ni(n,i)ijn[j2](nij)φ(j)


枚举 (i,n)

ans=i=1ni(n,i)ijn[j2](nij)φ(j)=dndi=1n/d[(i,n/d)=1]iijn/d[j2](n/dij)φ(j)=dnddDnμ(D)D2i=1n/dDiijn/dD[j2](n/dDij)φ(j)=TnTDTμ(D)Di=1n/Tiijn/T[j2](n/Tij)φ(j)=Tnf(T)g(n/T)=(fg)(n)

其中 f(T)=TdTμ(d)d,g(n)=i=1niijn[j2](nij)φ(j)

此时若计算出 f(1n),g(1n),注意到显然 f 是积性函数。

1n 时的 ans 做个快速狄利克雷卷积即可,狄卷的复杂度是 O(nloglogn)


考虑求 f,记 f0(n)=f(n)/n,则可以如下线性筛求 f0

f0(np)={1p(n=1)f0(n)f0(p)(pn)f0(n)(pn)


考虑求 g,扣掉 [j2],则多算的部分为:i=1ni(ni)=(n+13)

于是 g(n)=h(n)(n+13),h(n)=ijni(nij)φ(j)

h(n)=s=1n(ns)dsdφ(n/d)=s=1n(ns)F(s),其中 F(s)=dsdφ(n/d)

可以如下线性筛求 F

F(np)={2p1(n=1)F(n)F(p)(pn)p(2F(n)pF(n/p))(pn)

然后随便前缀和一下就能求出 h 了。

于是 f,g 都可以线性求,最后狄利克雷卷积一下做完。


留几道思考问题:

  • 如何证明 f 是积性函数?

  • 如何能推出线性筛求 f0,F 的式子?

  • 请尝试论证 F(1n) 的值能在 int 范围内存下,即线性筛的过程中不需要取模。

  • 一种及其巧妙的把 F 转化为 g 的方法为:令 F0(n)=F(n)n
    F0 做两次前缀和得到 F,此时 F(n)=F(n1),请证明并尝试正向推出此做法。

code
#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;
}
posted @   HaHeHyt  阅读(102)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示
主题色彩
主题色彩