Files
luogu/solutions/P6650.md
T

192 lines
5.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
## 题意简述
给定长度为 $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 <bits/stdc++.h>
using namespace std;
int n, k;
int a[100005];
int spf[100005]; // 最小质因子
vector<int> fac[100005]; // 每个数的质因子列表(带重数)
int cnt[100005]; // 当前窗口内每个质数的总指数
long long cur, ans;
deque<int> 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;
}
```