P6055 [RC-02] GCD 另解
P6055 [RC-02] GCD 另解
首先,使用你强大的注意力发现,好像对于 \(p,q\) 的枚举很像是在枚举 \(\gcd(p,q)=j\) 的对,将其还原回去,然后考虑对于一对 \(p,q\),考虑不再枚举 \(j\),那么式子可以化成 \([\gcd(i,\gcd(p,q))=1]\)。也就是说,要求解的东西是,有多少三元组 \((i,j,k)\) 满足 \(\gcd(i,j,k)=1\) 且 \(i,j,k\in [1,n]\)。我们设这个答案是 \(S(n)=\sum_{d=1}^n\mu(d)\lfloor \frac{n}d\rfloor^3\)。
一般做法考虑杜教筛 \(\mu\),我们直接杜教筛 \(S\)!其实感觉已经完全不是杜教筛了。
这好像不是数论函数前缀和吧!
但是其实我们只要满足(下式显然成立)
中的 \(\sum_{i=1}^n g(i)S(\lfloor \frac{n}{i}\rfloor)\) 可以快速计算,以及 \(g(i)\) 的前缀和可以快速计算,那么就可以使用杜教筛来进行计算,\(S\) 可以不必是前缀和。
我们此处取 \(g(i)=1\),显然,\(g(i)\) 的前缀和可以快速计算。那么 \(\sum_{i=1}^n g(i)S(\lfloor \frac{n}{i}\rfloor)\) 能快速计算吗?
我们发现 \(\sum_{i=1}^n g(i)S(\lfloor \frac{n}{i}\rfloor)=n^3\),因为 \(i\) 的枚举相当于将 \((i,j,k)\) 三元组按照 \(\gcd(i,j,k)\) 的值进行分类,每个三元组都会在其 \(\gcd\) 处被统计一次,那么这部分也搞定了,我们考虑形式证明此结论:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll P=998244353,B=1000;
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/hash_policy.hpp>
using namespace __gnu_pbds;
unordered_map<int,int> cache;
inline int add(ll a,ll b){return (a+b>=P?a+b-P:a+b);}
int F(int n){
if(n==0) return 0;
if(cache.find(n)!=cache.end()) return cache[n];
int c=1,j=2;
int k1=n/j,j2;
while(k1>1){
j2=n/k1+1;
c=add(c,1ll*(j2-j)*F(k1)%P);
j=j2,k1=n/j2;
}
return cache[n]=add(1ll*n*(1ll*n*n%P-1)%P,add(P-c,j));
}
int main(){
int n;cin>>n;
int res=F(n);
cout<<(res%P+P)%P;
}
/*FOOTNOTE OEIS 神秘做法 */
虽然已经和杜教筛大相径庭,但是复杂度分析是一样的。复杂度为 \(\mathcal O(n^{\frac{3}4})\),似乎无法使用预处理方式优化,理论能过,实际上有点卡常。本题所涉及数列是 OEIS A071778,本做法来源于 OEIS 讨论区,作者是来自加州大学伯克利分校的 Chai Wah Wu。
本文来自博客园,作者:haozexu,转载请注明原文链接:https://www.cnblogs.com/haozexu/p/18931668

浙公网安备 33010602011771号