P9308 「DTOI-5」#1f1e33 题解
声明:截止 \(2024.8.1\),拿下洛谷最优解最短解,代码长度不到 1k。复杂度 \(O(n\log \log n)\)。
先骂:官方题解菜!这种纯洁的数论题居然敢引入 NTT 作为标算,说明出题人不会推式子。
还有题解区一车 \(\log\) 的题解凭啥顶那么上面,推的一坨狗屎推出来的复杂度还不优秀。
说明:下面除法默认下取整,为了方便部分分数用 \(/\) 代替,若无特殊说明 \((i,j)\) 都表示取 \(\gcd\)。
下文记当前求的 \(f(n)\) 为 \(ans\),\(f\) 后面设成了其他函数。
套路性的枚举 \(\gcd\):\(ans=\sum\limits_{d=1}^n\sum\limits_{1\le j,k,j+k\le n/d}[(j,k)=1]\text{lcm}(n-d(j+k),d)\)。
注意到:\(\text{lcm}(n-d(j+k),d)=\dfrac{d(n-d(j+k))}{\gcd(n-d(j+k),d)}=\dfrac{d}{(d,n)}\cdot (n-d(j+k))\)。
\(ans=\sum\limits_{d=1}^n\dfrac{d}{(d,n)}\sum\limits_{1\le j,k,j+k\le n/d}[(j,k)=1](n-d(j+k))\),枚举 \(s=j+k\),枚举 \(j\),则 \((j,k)=(s,j)\):
\(ans=\sum\limits_{d=1}^n\dfrac{d}{(d,n)}\sum\limits_{s=2}^{n/d}(n-ds)\sum\limits_{j=1}^{s-1} [(s,j)=1]=\sum\limits_{d=1}^n\dfrac{d}{(d,n)}\sum\limits_{s=2}^{n/d}(n-ds)\varphi(s)=\sum\limits_{i=1}^n\dfrac{i}{(n,i)}\sum\limits_{ij\le n} [j\ge 2](n-ij)\varphi(j)\)。
枚举 \((i,n)\):
其中 \(f(T)=T\sum\limits_{d\mid T}\mu(d)d,g(n)=\sum\limits_{i=1}^{n}i\sum\limits_{ij\le n} [j\ge 2](n-ij)\varphi(j)\)。
此时若计算出 \(f(1\sim n),g(1\sim n)\),注意到显然 \(f\) 是积性函数。
求 \(1\sim n\) 时的 \(ans\) 做个快速狄利克雷卷积即可,狄卷的复杂度是 \(O(n\log\log n)\)。
考虑求 \(f\),记 \(f_0(n)=f(n)/n\),则可以如下线性筛求 \(f_0\):
\(f_0(np)=\begin{cases}1-p(n=1)\\f_0(n)f_0(p)(p\nmid n)\\f_0(n)(p\mid n)\end{cases}\)
考虑求 \(g\),扣掉 \([j\ge 2]\),则多算的部分为:\(\sum\limits_{i=1}^n i(n-i)=\dbinom{n+1}{3}\)。
于是 \(g(n)=h(n)-\dbinom{n+1}{3},h(n)=\sum\limits_{ij\le n} i(n-ij)\varphi(j)\)。
\(h(n)=\sum\limits_{s=1}^n(n-s)\sum\limits_{d\mid s}d\varphi(n/d)=\sum\limits_{s=1}^n(n-s)F(s)\),其中 \(F(s)=\sum\limits_{d\mid s}d\varphi(n/d)\)。
可以如下线性筛求 \(F\):
\(F(np)=\begin{cases}2p-1(n=1)\\F(n)F(p)(p\nmid n)\\p(2F(n)-pF(n/p))(p\mid n)\end{cases}\)
然后随便前缀和一下就能求出 \(h\) 了。
于是 \(f,g\) 都可以线性求,最后狄利克雷卷积一下做完。
留几道思考问题:
-
如何证明 \(f\) 是积性函数?
-
如何能推出线性筛求 \(f_0,F\) 的式子?
-
请尝试论证 \(F(1\sim n)\) 的值能在
int
范围内存下,即线性筛的过程中不需要取模。 -
一种及其巧妙的把 \(F\) 转化为 \(g\) 的方法为:令 \(F_0(n)=F(n)-n\)。
对 \(F_0\) 做两次前缀和得到 \(F'\),此时 \(F(n)=F'(n-1)\),请证明并尝试正向推出此做法。
$\texttt{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;
}