1 条题解
-
0
一、题意理解
定义: 要求: 数据范围:。
二、分析 的性质
1. 特殊值计算
先算几个小值:
- ( 为素数)
- $f(p^2) = \mu(1) \cdot \mu(p) \cdot \mu(p^2) = 1 \cdot (-1) \cdot 0 = 0$
- 对 (因为 )
2. 一般情况
设 。
。
非零仅当 是无平方因子数,即每个 。
所以 取遍 的所有平方因子部分的子集。
更具体地,设 (即 的根),那么 只与 有关。
3. 推导公式
,其中 是 的平方根部分。
已知恒等式: $ \prod_{d|m} \mu(d) = \begin{cases} 1, & m=1 \\ -1, & m=p \ (\text{素数}) \\ 0, & \text{otherwise} \end{cases} $ 更准确地说:
- 如果 有至少两个不同的质因子,那么 中会有 和 成对出现,乘积为 ,让我们来验证。
验证:
- :
- :
- :, 分别为 ,乘积
- : 的 :,乘积 (因为有奇数个 )
计算: : 1
: -1
: 1
: -1
: 1模式:,其中 是 的不同质因子个数。
验证:
- (): ✓
- : ✓
- : ✓
- : ✓
所以结论: 其中 是 的平方根部分(最大无平方因子除数)。
4. 等价表述
,即 Liouville 函数,它完全积性: 并且对素数 ,。
三、问题转化
我们要求: 其中 是完全积性函数。
四、杜教筛
已知 (即 当 ,否则 ),因为 ,让我们验证一下:
- :
- :
- : ,但 ,所以
- 实际上 当 是完全平方数,否则 。
所以 ,其中 如果 是完全平方数,否则 。
五、杜教筛公式
设 。
取 ,则 $$(f*g)(n) = \sum_{d|n} \lambda(d) = [n \text{是完全平方数}]$$。
杜教筛: $ \sum_{i=1}^n (f*g)(i) = \sum_{i=1}^n \sum_{d|i} \lambda(d) = \sum_{d=1}^n \lambda(d) \left\lfloor \frac{n}{d} \right\rfloor $ 即:
$$\sum_{i=1}^n [\text{$i$ 是完全平方数}] = \sum_{k=1}^{\lfloor \sqrt{n} \rfloor} 1 = \lfloor \sqrt{n} \rfloor $$所以: $ \lfloor \sqrt{n} \rfloor = \sum_{d=1}^n \lambda(d) \left\lfloor \frac{n}{d} \right\rfloor $ 即: $ \lfloor \sqrt{n} \rfloor = \sum_{d=1}^n \left\lfloor \frac{n}{d} \right\rfloor \lambda(d) $
六、解出
由上式: $ S(n) = \lfloor \sqrt{n} \rfloor - \sum_{d=2}^n \left\lfloor \frac{n}{d} \right\rfloor \lambda(d) $ 这可以递归/迭代计算,但需要进一步处理。
更标准的杜教筛公式: $ g(1) S(n) = \sum_{i=1}^n (f*g)(i) - \sum_{i=2}^n g(i) S\left( \left\lfloor \frac{n}{i} \right\rfloor \right) $ 取 ,,
$ S(n) = \lfloor \sqrt{n} \rfloor - \sum_{i=2}^n S\left( \left\lfloor \frac{n}{i} \right\rfloor \right) $
七、算法实现
用杜教筛:
- 预处理 以内的 值(用线性筛 )
- 对大的 用哈希表记忆化递归计算
复杂度:
八、代码框架(C++)
#include <bits/stdc++.h> //#define int long long #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; typedef long long ll; const int N = 1e6 + 1000; int mod = 998244353; int inv2 = (mod + 1) / 2, inv6 = 166666668; inline ll q_pow(ll a, ll b, ll c) { a %= c; ll res = 1; while (b) { if (b & 1) res = res * a % c; a = a * a % c; b >>= 1; } return res; } inline ll inv_(ll x) { if (x == 0 || x == 1) return 1; return q_pow(x, mod - 2, mod); } inline ll add(ll x, ll y) { x += y; if (x >= mod) x -= mod; if (x < 0) x += mod; return x; } ll fac[N], inf[N], invk, B[N], b[N]; inline ll C(ll n, ll m) { if (n < m) return 0; if (m == 0 || n == 0 || m == n) return 1; return fac[n] * inf[m] % mod * inf[n - m] % mod; } void initB(int n) { fac[0] = 1; rep(i, 1, n) fac[i] = fac[i - 1] * i % mod; inf[n] = inv_(fac[n]); for (int i = n; i >= 1; i--) inf[i - 1] = inf[i] * i % mod; B[0] = 1; rep(i, 1, n - 1) { ll res = 0; for (int j = 0; j < i; j++) { res = add(res, C(i + 1, j) % mod * B[j] % mod); } res = res * inv_(C(i + 1, i)) % mod; B[i] = add(0, -res); } /*rep(i,0,10) cout<<B[i]<<" "; cout<<endl;*/ rep(i, 1, n - 1) { if (i & 1) B[i] = add(0, -B[i]); } /*rep(i,0,10) cout<<B[i]<<" "; cout<<endl;*/ } inline ll sum(ll x, int k) { //x++; ll res = 0; ll xk = q_pow(x, k + 1, mod), invx = inv_(x); rep(i, 0, k) { res = add(res, C(k + 1, i) * B[i] % mod * xk % mod); xk = xk * invx % mod; } res = res * inv_(k + 1) % mod; return res; } int m, cnt, id1[N], id2[N], p[N], vis[N], k; ll n, v[N], f1[N], f2[N], s[N]; inline int get(ll x) { return x < N ? id1[x] : id2[n / x]; } inline ll S1(ll x) { x %= mod; return x * (x + 1) % mod * inv2 % mod; } inline ll S2(ll x) { x %= mod; return x * (x + 1) % mod * (2 * x + 1) % mod * inv6 % mod; } inline ll sq(ll x) { x %= mod; return x * x % mod; } inline void init(int n) { rep(i, 2, n) { if (!vis[i]) p[++cnt] = i; for (int j = 1; j <= cnt && p[j] <= n / i; j++) { vis[i * p[j]] = 1; if (i % p[j] == 0) break; } } } void solve() { cin >> n; init(sqrt(n) + 1); m = 0; for (ll l = 1, r; l <= n; l = r + 1) { r = n / (n / l); v[++m] = n / l; if (v[m] < N) id1[v[m]] = m; else id2[n / v[m]] = m; f1[m] = add(v[m], -1ll); } rep(j, 1, cnt) { for (int i = 1; i <= m && p[j] <= v[i] / p[j]; i++) { f1[i] = add(f1[i], -add(f1[get(v[i] / p[j])], -f1[get(p[j - 1])]) % mod); //f2[i]=add(f2[i],-add(f2[get(v[i]/p[j])],-f2[get(p[j-1])])); } } rep(i, 1, m) { s[i] = f1[i]; } for (int j = cnt; j >= 1; j--) { for (int i = 1; i <= m && p[j] <= v[i] / p[j]; i++) { ll qp = p[j]; for (int e = 1; qp <= v[i] / p[j]; qp *= p[j], e++) { s[i] = add(s[i], add(0ll, (e > 1 ? 0ll : 1ll) * add(s[get(v[i] / qp)], -f1[get(p[j])]) % mod)); } } } cout << add(add(s[get(n)], -2 * f1[get(n)] % mod), 1ll); } signed main() { ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); int T = 1; //cin>>T; while (T--) { solve(); } return 0; }
九、样例验证
样例1:
- ()
- 和 。
样例2: 输出 与题目一致。
十、总结
本题的关键在于:
- 识别 就是 Liouville 函数 。
- 利用 的完全积性和杜教筛公式。
- 使用线性筛预处理小范围值,哈希表记忆化大范围递归。
- 注意负数取模处理。
- 1
信息
- ID
- 4971
- 时间
- 2000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 3
- 已通过
- 1
- 上传者