From f0f909a03908c88ca81dac065f99dda2856cbe91 Mon Sep 17 00:00:00 2001 From: hanyixuanten <105723997+hanyixuanten@users.noreply.github.com> Date: Mon, 21 Sep 2026 20:44:46 +0800 Subject: [PATCH] new file: solutions/P6650.md --- solutions/AT_agc013_e.md | 2 +- solutions/P6650.md | 192 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 193 insertions(+), 1 deletion(-) create mode 100644 solutions/P6650.md diff --git a/solutions/AT_agc013_e.md b/solutions/AT_agc013_e.md index db8be77..b92e416 100644 --- a/solutions/AT_agc013_e.md +++ b/solutions/AT_agc013_e.md @@ -58,7 +58,7 @@ int main() } ``` -## 90 pts O(m log n) +## 100 pts O(m log n) 注意到一个区间内需要多次转移,非常浪费。可以考虑使用矩阵快速幂优化。 diff --git a/solutions/P6650.md b/solutions/P6650.md new file mode 100644 index 0000000..18bf91a --- /dev/null +++ b/solutions/P6650.md @@ -0,0 +1,192 @@ +## 题意简述 + +给定长度为 $n$ 的序列 $a$ 和整数 $k$。选择一个区间 $[l,r]$,满足区间最大值与最小值之差不超过 $k$。 + +将区间乘积质因数分解: + +$$ +\prod_{i=l}^r a_i = \prod_p p^{E_p}。 +$$ + +对于每个质数 $p$,把 $E_p$ 拆成若干个互不相同的正整数 $e_{p,1},e_{p,2},\dots,e_{p,t_p}$,并选取对应的质数幂 $p^{e_{p,i}}$。所有选取的数必须互不相同。 + +得分为所有选取数的约数个数之和: + +$$ +\sum_p \sum_i (e_{p,i}+1)。 +$$ + +求最大得分。 + +## 思路分析 + +### 单个质数的贡献 + +固定一个质数 $p$,设它在区间乘积中的总指数为 $E$。 + +若把 $E$ 拆成 $t$ 个互不相同的正整数,则这些数的和至少为 + +$$ +1+2+\cdots+t = \frac{t(t+1)}2。 +$$ + +当 $E \ge \frac{t(t+1)}2$ 时,可以取 $1,2,\dots,t-1$ 以及 $E-\frac{t(t-1)}2$,它们互不相同且总和为 $E$。 + +因此最大可行的 $t$ 为满足 $\frac{t(t+1)}2 \le E$ 的最大整数,即 + +$$ +t_{\max} = \left\lfloor \frac{\sqrt{8E+1}-1}{2} \right\rfloor。 +$$ + +对应的得分为 + +$$ +\sum_i (e_i+1) = E + t_{\max}。 +$$ + +定义 + +$$ +gx(E) = E + \left\lfloor \frac{\sqrt{8E+1}-1}{2} \right\rfloor, +$$ + +则一个质数指数为 $E$ 时的贡献为 $gx(E)$。特别地,$gx(0)=0$。 + +### 区间选择 + +对于固定的右端点 $r$,如果区间 $[l,r]$ 合法,那么区间 $[l+1,r]$ 也合法,因为子区间的极差不会增大。 + +又因为窗口扩大时,每个质数的指数 $E_p$ 只会增加或不增加,同时 $gx(E)$ 单调递增,所以固定 $r$ 时,区间越长得分越高。于是只需要找到最小的合法左端点。 + +使用双指针维护滑动窗口。令左端点 $l$ 单调右移,并用两个单调队列分别维护当前窗口的最大值和最小值。当窗口极差超过 $k$ 时,不断右移 $l$,同时删除 $a_l$ 的质因子贡献。 + +## 算法流程 + +1. 预处理最小质因子 $spf$,用于快速分解每个 $a_i$。 +2. 将每个 $a_i$ 分解为质因子列表,保留重数。 +3. 维护滑动窗口 $[l,r]$,其中: + - $cnt[p]$ 表示当前窗口内质数 $p$ 的总指数; + - $cur$ 表示当前窗口得分 $\sum_p gx(cnt[p])$; + - 两个单调队列维护窗口最大值和最小值。 +4. 枚举右端点 $r$: + - 将 $a_r$ 的质因子加入窗口,更新 $cnt$ 和 $cur$; + - 将 $r$ 加入单调队列; + - 若当前窗口极差超过 $k$,则不断右移 $l$,并删除 $a_l$ 的质因子贡献; + - 用当前 $cur$ 更新答案。 +5. 输出答案。 + +## 复杂度分析 + +设 $A=\max a_i \le 10^5$。 + +- 预处理最小质因子:$O(A \log \log A)$。 +- 分解所有 $a_i$:总质因子个数为 $O(n \log A)$。 +- 滑动窗口:每个元素的每个质因子加入一次、删除一次,复杂度 $O(n \log A)$。 +- 单调队列:$O(n)$。 + +总时间复杂度为 + +$$ +O(A \log \log A + n \log A)。 +$$ + +## 参考代码 + +```cpp +#include +using namespace std; + +int n, k; +int a[100005]; +int spf[100005]; // 最小质因子 +vector fac[100005]; // 每个数的质因子列表(带重数) +int cnt[100005]; // 当前窗口内每个质数的总指数 +long long cur, ans; +deque dq_max, dq_min; + +// 计算 e 的贡献 +long long gx(int e) { + if (e == 0) return 0; + long long t = (sqrt(8.0 * e + 1) - 1) / 2; + while ((t + 1) * (t + 2) / 2 <= e) ++t; + while (t * (t + 1) / 2 > e) --t; + return e + t; +} + +// 预处理最小质因子 +void init_spf() { + for (int i = 2; i < 100005; ++i) { + if (!spf[i]) { + spf[i] = i; + if ((long long)i * i < 100005) { + for (int j = i * i; j < 100005; j += i) { + if (!spf[j]) spf[j] = i; + } + } + } + } +} + +// 分解质因数 +void get_fac(int idx, int x) { + while (x > 1) { + int p = spf[x]; + while (x % p == 0) { + fac[idx].push_back(p); + x /= p; + } + } +} + +int main() { + scanf("%d%d", &n, &k); + init_spf(); + + for (int i = 1; i <= n; ++i) { + scanf("%d", &a[i]); + get_fac(i, a[i]); + } + + int l = 1; + cur = 0, ans = 0; + + for (int r = 1; r <= n; ++r) { + // 加入 a[r] + for (int p : fac[r]) { + int e = cnt[p]; + cur += gx(e + 1) - gx(e); + cnt[p]++; + } + + while (!dq_max.empty() && a[dq_max.back()] <= a[r]) dq_max.pop_back(); + dq_max.push_back(r); + + while (!dq_min.empty() && a[dq_min.back()] >= a[r]) dq_min.pop_back(); + dq_min.push_back(r); + + // 收缩左边界 + while (1) { + while (!dq_max.empty() && dq_max.front() < l) dq_max.pop_front(); + while (!dq_min.empty() && dq_min.front() < l) dq_min.pop_front(); + + int mx = a[dq_max.front()]; + int mn = a[dq_min.front()]; + + if (mx - mn <= k) break; + + // 移除 a[l] + for (int p : fac[l]) { + int e = cnt[p]; + cur += gx(e - 1) - gx(e); + cnt[p]--; + } + l++; + } + + ans = max(ans, cur); + } + + printf("%lld\n", ans); + return 0; +} +``` \ No newline at end of file