LOJ #6222. 幂数 !(加强版)

发布时间 2023-04-28 10:30:20作者: icyM3tra

题目链接

题意

给定整数 \(n(1\le n\le 10^{25})\),求 \(n\) 以内 Powerful Number 的个数,以及它们的和。

题解

Part 1

如果 \(x\) 是一个 Powerful Number,那么它一定可以表示成 \(a^2b^3\) 的形式。

我们限制 \(b\) 不含(大于 \(1\) 的)平方因子,那么表示方案唯一。

证明是简单的,考虑 \(x\) 每一项质因子 \(p^k(k\ge 2)\) 如何分配即可。

如果 \(k\) 是偶数,将 \(p^k\) 全部分配给 \(a^2\)
如果 \(k\) 是奇数,将 \(p^{k-3}\) 分配给 \(a^2\),将 \(p^3\) 分配给 \(b^3\)

显然 \(b\) 的每个质因子的幂次最多为 \(1\),并且这是唯一的分配方案。证毕。

至此我们可以将所求转化为易于计算的式子:

\[\begin{aligned} cnt&=\sum_{i^2j^3~\le n}\mu^2(j)\\ sum&=\sum_{i^2j^3~\le n}\mu^2(j)i^2j^3 \end{aligned}\]

其中 \(\mu\)莫比乌斯函数

Part 2

先来看 \(cnt\) 怎么算。

我们引入一个重要的优化求和的 trick:

取一对实数 \(x,y\),使得 \(x^2y^3=n\)

\(i^2j^3\le n\),则 \(i\le x\)\(j\le y\) 中至少有一个成立。

\(i\le x\)\(j\le y\) 的情况分别求和,再减去公共部分。式子如下:

\[cnt=\sum_{i=1}^x\sum_{j=1}^{\sqrt[3]{n/i^2}}\mu^2(j)+\sum_{j=1}^y\sum_{i=1}^{\sqrt{n/j^3}}\mu^2(j)-\sum_{i=1}^x\sum_{j=1}^y\mu^2(j) \]

\(\displaystyle{\rm S}\mu^2(n)=\sum_{i=1}^n\mu^2(i)\),那么式子可以写成:

\[cnt=\sum_{i=1}^x{\rm S}\mu^2\left(\sqrt[3]{n/i^2}\right)+\sum_{j=1}^y\mu^2(j)\sqrt{n/j^3}-x\cdot {\rm S}\mu^2(y) \]

预处理之后可以 \(O(x+y)\) 计算。

然而预处理的复杂度是 \(O(\sqrt[3]n+y)\),不能接受。

再考虑一个用于计算 \({\rm S}\mu^2\) 的恒等式:

\[\sum_{i=1}^n\mu^2(i)=\sum_{i=1}^{\sqrt n}\mu(i)(n/i^2) \]

证明:

实际上,\(\displaystyle\mu^2(i)=\sum_{j^2~|i}\mu(j)\)

\(i\) 的最大平方因子为 \(t\),则 \(\displaystyle\sum_{j^2~|i}\mu(j)=\sum_{j|t}\mu(j)=[t=1]=\mu^2(i)\)

用上式代换 \(\mu^2(i)\),化简:

\[\sum_{i=1}^n\mu^2(i)=\sum_{i=1}^n\sum_{j^2~|i}\mu(j)=\sum_{j=1}^{\sqrt n}\mu(j)(n/j^2) \]

证毕。

(本质上就是对平方因子进行容斥)

如果直接用这个式子计算,那么求 \({\rm S}\mu^2(n)\) 的复杂度是 \(O(\sqrt n)\)。事实上可以继续优化:

\[\begin{aligned} {\rm S}\mu^2(n)&=\sum_{i=1}^n\mu(i)(n/i^2)\\ &=\sum_{i^2j\le n}\mu(i)\\ \end{aligned}\]

\(m=\sqrt[3]n\),套用前面的 trick:

\[{\rm S}\mu^2(n)=\sum_{i=1}^m\mu(i)(n/i^2)+\sum_{j=1}^m{\rm S}\mu\left(\sqrt{n/j}\right)-m\cdot {\rm S}\mu(m) \]

\(O(\sqrt n)\) 的预处理之后,我们可以 \(O(\sqrt[3]n)\) 求出 \({\rm S}\mu^2(n)\)

回忆一下 \(cnt\) 的计算式:

\[cnt=\sum_{i=1}^x{\rm S}\mu^2\left(\sqrt[3]{n/i^2}\right)+\sum_{j=1}^y\mu^2(j)\sqrt{n/j^3}-x\cdot {\rm S}\mu^2(y) \]

现在我们的复杂度变为 \(\displaystyle O\left(y+\sum_{i=1}^x\sqrt[9]{n/i^2}\right)\)

积分一下,也就是 \(O(y+\sqrt[9]{nx^7})\)。其中 \(x^2y^3=n\)

为了平衡复杂度,可以通过均值不等式或者求导来寻找最值。

易得 \(x=O(n^{2/13})\) 时复杂度最优,为 \(O(n^{3/13})\)

Part 2

\(sum\) 的处理方法与 \(cnt\) 基本一致。我们快速过一遍式子。

\({\rm id}^k(n)=n^k,f(n)=\mu^2(n)n^3\)

\[\begin{aligned} sum&=\sum_{i^2j^3~\le n}\mu^2(j)i^2j^3\\ &=\sum_{i^2j^3~\le n}{\rm id}^2(i)f(j)\\ &=\sum_{i=1}^x{\rm id}^2(i){\rm S}f\left(\sqrt[3]{n/i^2}\right)+\sum_{j=1}^yf(j){\rm Sid}^2\left(\sqrt{n/j^3}\right)-{\rm Sid}^2(x){\rm S}f(y) \end{aligned}\]

其中 \({\rm Sid}^2(n)=\dfrac{n(n+1)(2n+1)}6\) 可以直接计算。

对于 \({\rm S}f(n)\),我们有:

\[\begin{aligned} {\rm S}f(n)&=\sum_{i=1}^n\mu^2(i)i^3\\ &=\sum_{i=1}^ni^3\sum_{j^2~|i}\mu(j)\\ &=\sum_{j=1}^{\sqrt n}\mu(j)j^6{\rm Sid}^3(n/j^2)\\ &=\sum_{ij^2~\le n}{\rm id}^3(i)\mu(j)j^6 \end{aligned}\]

\(g(n)=\mu(j)j^6\)

\[\begin{aligned} {\rm S}f(n)&=\sum_{i=1}^m{\rm id}^3(i)Sg\left(\sqrt{n/i}\right)+\sum_{j=1}^mg(j){\rm Sid}^3(n/j^2)-{\rm Sid}^3(m){\rm S}g(m) \end{aligned}\]

其中 \({\rm Sid}^3(n)=\left(\dfrac{n(n+1)}2\right)^2\) 同样可以直接计算。

\({\rm S}g\) 直接预处理即可,复杂度为 \(O(\sqrt[6]n)\)

Part 3

到这里就做完了。

时间复杂度 \(O(n^{3/13})\),空间复杂度 \(O(n^{3/13})\)

代码实现如下。需要注意开方的精度问题。

#include<bits/stdc++.h>
#define rep(i,l,r) for (int i=l; i<=r; ++i)
typedef long long ll;
typedef __int128 Lll;

const int N=6e5+5,M=15005;
int t1; ll cnt,t2; Lll sum;
std::bitset<N>mu2;
int mu[N],smu[N]; Lll g[M],sg[M];

inline Lll read() {
    Lll x=0; char ch=getchar();
    while (!isdigit(ch)) ch=getchar();
    while (isdigit(ch)) x=x*10+(ch^48),ch=getchar();
    return x;
}
void write(Lll x) {
    if (x>9) write(x/10);
    putchar(x%10|48);
}

void prework(const int n,const int m) {
    mu[1]=1;
    rep(i,2,n) if (!mu[i])
        for (int j=i; j<=n; j+=i) mu[j]=(mu[j]<0?1:-1);
    for (int i=2,t; (t=i*i)<=n; ++i) if (mu[t])
        for (int j=t; j<=n; j+=t) mu[j]=0;
    rep(i,1,n) {
        smu[i]=smu[i-1];
        if (mu[i]) mu2[i]=1,smu[i]+=mu[i];
    }
    rep(i,1,m) {
        sg[i]=sg[i-1];
        if (mu[i]) sg[i]+=(g[i]=mu[i]*i*Lll(i)*i*i*i*i);
    }
}

inline Lll S2(ll x) { return x*Lll(x+1)*(x<<1|1)/6; }
inline Lll S3(ll x) { return x=x*(x+1)>>1,Lll(x)*x; }
inline ll Sqrt(Lll x) { // 精确开方
    ll t=sqrtl(x)-2;
    while (Lll(t)*t<=x) ++t; //在 sqrtl 基础上微调
    return t-1;
}
inline int Cbrt(Lll x) {
    int t=cbrtl(x)-2;
    while (Lll(t)*t*t<=x) ++t;
    return t-1;
}

int Smu2(const int n) {
    const int m=cbrt(n); int s=0,t;
    rep(i,1,m) t=n/i,s+=t/i*mu[i]+smu[(int)sqrt(t)];
    return s-m*smu[m];
}
Lll Sf(const int n) {
    const int m=cbrt(n); Lll s=0; int t;
    rep(i,1,m) t=n/i,s+=S3(t/i)*g[i]+sg[(int)sqrt(t)]*i*i*i;
    return s-S3(m)*sg[m];
}
int main() {
    const Lll n=read();
    const int x=pow(n,2./13),y=cbrtl(n/(x*x));
    prework(y+100,pow(n,1./6)+100);
    rep(i,1,x)
        t1=Cbrt(n/(i*i)),
        cnt+=Smu2(t1),sum+=Sf(t1)*i*i;
    rep(i,1,y) if (mu2[i])
        t2=Sqrt(n/(ll(i)*i*i)),
        cnt+=t2,sum+=S2(t2)*i*i*i;
    printf("%lld\n",cnt-ll(x)*Smu2(y));
    write(sum-S2(x)*Sf(y));
    return 0;
}