当前位置: 首页 > news >正文

P9920 学习笔记

五年级蒟蒻的第一篇黑题题解,求过,求赞。

顺着做题计划做题,顺便补一下这周掉的一点点社贡分。

这个题跟我从早上 \(8\) 点纠缠到晚上 \(10\) 点,题挺好,就是这个 Subtask 分治有点恶心

题意

给定一个积性函数 \(f(x)\),但不保证 \(f(1)=1\),给定莫比乌斯变换

\[g(x)=\sum_{d \mid x} f(d) \]

中的 \(k\) 组值,接下来 \(t\) 组询问,每组询问给定一个 \(n\),求 \(f(n) \bmod 998244353\)

前置芝士

  • 常见狄利克雷卷积;
  • 莫比乌斯函数 \(\mu(n)\)
  • Miller-Rabin 素性测试;
  • Pollard-rho 大数分解。

下面将逐一分析每一个 Subtask 的解法。

本文中的质因数分解(试除法,Miller-Rabin 素性测试,Pollard_rho 大数分解)为简洁,均用一个函数实现,相关函数如下:

std::mt19937_64rng(std::chrono::steady_clock::now().time_since_epoch().count());
bool Miller_Rabin(std::int64_t n){if (n < 2 || !(n & 1)) return false;if (n == 2 || n == 3) return true;std::int64_t d = n - 1, s = 0;while (!(d & 1)) d >>= 1, ++s;static std::int64_t bases[] = {2, 325, 9375, 28178, 450775, 9780504, 1795265022};for (std::int64_t a : bases)if (a % n){std::int64_t x = binpow(a, d, n); bool flag = false;if (x == 1 || x == n - 1) continue;for (std::int64_t r = 1; r < s; r++){x = (int128)x * x % n;if (x == n - 1){ flag = true; break; }}if (!flag) return false;}return true;
}
std::int64_t Pollard_rho(std::int64_t n){if (!(n & 1)) return 2;if (!(n % 3)) return 3;while (true){std::int64_t c = std::uniform_int_distribution <std::int64_t> (1, n - 1) (rng);auto f = [&] (std::int64_t x){ return ((int128)x * x % n + c) % n; };std::int64_t x = std::uniform_int_distribution <std::int64_t> (2, n - 2) (rng);std::int64_t y = x, d = 1;while (d == 1) x = f(x), y = f(f(y)), d = std::gcd(std::abs(x - y), n);if (d != n) return d;}
} 
void fac_rec(std::int64_t n, std::map <std::int64_t, std::int64_t>& res){if (n == 1) return;if (Miller_Rabin(n)) return void(res[n]++);std::int64_t d = Pollard_rho(n);fac_rec(d, res), fac_rec(n / d, res);
}
std::map <std::int64_t, std::int64_t> fac(std::int64_t n){ // 均放在该函数中实现std::map <std::int64_t, std::int64_t> res;for (std::int64_t p = 2; p <= 1e6 && p * p <= n; p++)if (n % p == 0){std::int64_t exps = 0;while (n % p == 0) n /= p, ++exps;res[p] = exps;}if (n > 1){if (Miller_Rabin(n)) res[n] = 1;else fac_rec(n, res);}return res;
}

\(\text{Epsilion}\)

  • 数据范围:\(n \le 10^6,k=100,t=10\)

我们发现 \(g(n)=\varepsilon(n)\),于是 \(f=g*\mu=\varepsilon*\mu=\mu\),又因为 \(n=10^6\),线性筛预处理即可,时间复杂度 \(O(n)\)

void sieve(std::int64_t n){mu[1] = 1;for (std::int64_t i = 2; i <= n; i++){if (!vis[i]) prime[++tot] = i, mu[i] = -1;for (std::int64_t j = 1; j <= tot && i * prime[j] <= n; j++){vis[i * prime[j]] = true;if (i % prime[j] == 0){mu[i * prime[j]] = 0;break;} mu[i * prime[j]] = -mu[i];}}
}
void Epsilon(){sieve(maxn - 5);for (std::int64_t i = 1; i <= t; i++)if (query[i] == 1) std::cout << 1 << std::endl;else std::cout << (mu[query[i]] + mod) % mod << std::endl;
}

\(\text{Division}\)

  • 数据范围:\(n \le 10^{9},k=100,t=10\)

注意到 \(g(n)=d(n)\),从而 \(f(n)=1\),于是所有询问均输出 \(1\) 即可。

void Division(){for (std::int64_t i = 1; i <= t; i++)std::cout << 1 << std::endl;
}

\(\text{Unknown}\)

  • 数据范围:\(n \le 10^{18},k=1,t=10\)

这个 \(n\) 弄这么大是何意味呢,我们发现仅给出了 \(g(1)=0\),从而 \(f(1)=0\),积性得 \(f(n)=0\),于是所有询问均输出 \(0\) 即可。

void Unknown(){for (std::int64_t i = 1; i <= t; i++)std::cout << 0 << std::endl;
}

\(\text{Random}\)

  • 数据范围:\(n \le 10^5,k=10^5,t=10^5\)

我们观察数据范围发现 \(n \le k\),即所有询问的 \(n\) 均已给出,反演:

\[f(n)=g(n)-\sum_{d \mid n,d<n} f(d) \]

从小到大递推计算 \(f(i),1\le i\le k\) 即可,时间复杂度 \(O(k \log k)\)

void Random(){for (std::int64_t i = 1; i <= k; i++)g[giv[i].first] = giv[i].second % mod;f[1] = g[1];for (std::int64_t i = 2; i <= k; i++){f[i] = g[i];for (std::int64_t j = 1; j * j <= i; j++)if (i % j == 0){if (j != i) f[i] = (f[i] - f[j]) % mod;if ((i / j) != i && (i / j) != j) f[i] = (f[i] - f[i / j]) % mod;}}for (std::int64_t i = 1; i <= t; i++)if (query[i] <= k) std::cout << (f[query[i]] + mod) % mod << std::endl;else std::cout << 0 << std::endl;
}

\(\text{Double}\)

  • 数据范围:\(n \le 10^9,k=100,t=10\)

对于已知的 \(g\) 做一次莫比乌斯反演 \(h=g*\mu\),发现并没有什么屌用,再反演一次得到 \(p=h*\mu\),结果均为完全平方数,开方后得到 \(\varphi(n)^2\),从而 \(f=\varphi*1\),即

\[f(n)=\sum_{d \mid n} \varphi(d)^2 \]

由于 \(n \le 10^9\),我们直接对于询问做质因子分解,利用积性有

\[f(p^e)=\sum_{i=0}^e \varphi(p^i)^2=1+\sum_{i=1}^e (p^{i-1}(p-1))^2 \]

然后

\[f(n)=\prod f(p^e) \]

试除法试到 \(10^6\),剩余部分 Miller-Rabin 素性测试即可,可以做到 \(O(\sqrt n)\)

void Double(){auto pows = [&](std::int64_t p, std::int64_t e) ->std::int64_t{std::int64_t res = 1, phi = 1, pk = 1;for (std::int64_t i = 1; i <= e; i++){pk = pk * p % mod;if (i == 1) phi = (p - 1) % mod;else phi = phi * p % mod;res = (res + phi * phi) % mod;}return res;};for (std::int64_t i = 1; i <= t; i++){std::int64_t ans = 1;for (auto [p, e] : fac(query[i])) ans = ans * pows(p % mod, e) % mod;std::cout << ans << std::endl;}
}

\(\text{Hack}\)

  • 数据范围:\(n \le 10^9,k=31623,t=1\)

观察反演后的结果,有 \(f(p^k)=p^{2k-1}\),对于 \(n=pq\),其中 \(p,q\) 为质数,有 \(f(n)=n\)

我们对于询问分解质因数,如果仅有两个质因子且质数均为 \(1\),直接输出 \(n \bmod 998244353\),否则计算 \(\prod p^{2e-1} \bmod 998244353\),由于 \(n \le 10^9\),试除加上 Miller-Rabin 素性测试即可,可以做到 \(O(\sqrt n)\)

void Hack(){for (std::int64_t i = 1; i <= t; i++){auto fs = fac(query[i]);if (fs.size() == 2 && fs.begin()->second == 1 && std::next(fs.begin())->second == 1) std::cout << query[i] % mod << std::endl;else {std::int64_t ans = 1;for (auto [p, e] : fs) ans = ans * binpow(p % mod, 2 * e - 1, mod) % mod;std::cout << ans << std::endl;}}
}

\(\text{Square}\)

  • 数据范围:\(n \le 10^{18},k=100,t=5\)

我们发现 \(g(n)=n^2\),反演 \(f=g*\mu\),即

\[f(n)=\sum_{d \mid n} d^2\mu(\frac nd) \]

根据积性,有

\[f(p^k)=p^{2k}-p^{2k-2} \]

于是

\[f(n)=n^2 \prod_{p \mid n} (1-p^{-2}) \]

对于每个询问用 Pollard-Rho 分解质因数,计算上式,注意求逆元,这样速度快得飞起,为 \(O(n^{1/4})\)

void Square(){for (std::int64_t i = 1; i <= t; i++){auto fs = fac(query[i]);auto ans = (query[i] % mod) * (query[i] % mod) % mod;for (auto [p, e] : fs){std::int64_t inv = binpow(p % mod, mod - 2, mod);inv = inv * inv % mod;ans = ans * ((1 - inv + mod) % mod) % mod;}std::cout << ans << std::endl;}
}

\(\text{Poly}\)

  • 数据范围:\(n \le 10^9,k=10^5,t=100\)

注意到 \(f(p^k)\) 为关于 \(p^k\) 的三次多项式,即

\[f(p^k)=114p^{3k}+514p^{2k}+1919p^k+810 \]

(上述多项式系数由拉格朗日插值得出,参见学习笔记)

对询问的 \(n\) 分解质因数,对每个 \(p^e\) 代入多项式求值,乘起来即可,可以做到 \(O(\sqrt n)\)

\(\text{Thanks}\)

  • 数据范围:\(n \le 10^5,k=4,t=10^5\)

给了四个已知的 \(g\) 值,我们手算有 \(f(1)=f(2)=f(4)=1,f(3)=0,f(5)\) 无法确定,我们猜测 \(f(n)=n^2 \bmod 3\),符合积性。

void Thanks(){for (std::int64_t i = 1; i <= t; i++) std::cout << ((query[i] % 3) * (query[i] % 3) % 3) << std::endl;
}

无注释纯享版代码。

http://www.jsqmd.com/news/684223/

相关文章:

  • 2026年茶器销售行业靠谱GEO优化服务商核心能力选型分析报告 - 商业小白条
  • 一文速览最新发布的《CMMI中国2025优秀实践案例集》
  • STC89C52单片机玩转NE555:手把手教你实现一个简易频率计(附完整工程)
  • Day05-MySQL
  • 告别依赖噩梦:用Buildroot一键集成GStreamer到你的ARM-Linux系统镜像
  • 动态规划经典案例分析之编辑距离
  • 2026年3月升降货梯源头厂家推荐,液压货梯/升降平台/升降货梯,升降货梯源头厂家哪家性价比突出 - 品牌推荐师
  • “金三银四”春招大战正酣!2026职场招聘被AI点燃,岗位暴涨12倍,月薪超6万
  • 还在用 Visio 画架构图?这个 AI 神器让你告别手动绘图,5秒出图还能改!
  • 打卡信奥刷题(3150)用C++实现信奥题 P7682 [COCI 2008/2009 #5] TRESNJA
  • 服务型AI设计:从自助陷阱到智能服务革命
  • 2026 热镀锌桥架实测排行:全维度性能解析与工程采购落地指南 - 外贸老黄
  • 竞技性机器学习:核心优势与实战进阶指南
  • LeetCode 2024. 考试的最大困扰度【不定长滑窗】1643
  • 避开STC15定时器的那些坑:从模式选择到中断响应,我的调试笔记
  • 实战解析:基于GD32与ADS1118的高精度数据采集系统搭建
  • 2026 热镀锌桥架综合实力 TOP 测评:全维度品质实测与工程采购实操指南 - 外贸老黄
  • between的用法
  • 单片机控制板基础设计原则
  • 5分钟掌握SMUDebugTool:AMD Ryzen处理器硬件调试实战指南
  • 别再手动复制DLL了!Qt Creator + CMake一键配置OpenCV库(附完整配置流程)
  • LFM2.5-1.2B-Thinking轻量部署:Ollama系统提示词配置,让1.2B小模型发挥大能量
  • [Windows] Mouser v3.5.3第三方罗技鼠标驱动
  • 速看!黄金秘籍解决华为防火墙最困难的故障
  • 新手必看:CTFHub靶场RCE通关保姆级教程(从环境搭建到Flag获取)
  • 2026年AI生成式引擎优化行业梳理:五家值得企业选型参考的AI优化GEO服务商推荐 - 商业小白条
  • 往前走——成为更好的自己
  • 利用云函数做一个钉钉机器人提醒功能教程
  • Qwen3.5-2B赋能前端开发:自动生成JavaScript组件代码与文档
  • RWKV7-1.5B-world保姆级教程:Gradio界面日志导出功能,用于对话质量人工评估