1 条题解
-
0
自动搬运
来自洛谷,原作者为

Alex_Wei
**搬运于
2025-08-24 22:44:16,当前版本为作者最后更新于2023-02-09 17:52:45,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
很不错的题目,思路很自然,结论较难证但好猜,思维含量很高。
首先二分答案转化为 序列,目标是将所有数全部变成 。
基本性质:若可操作 且 ,则操作 一定更优。
设最优操作序列为 。
设 的权值为 , 的权值为 ,前缀和序列为 ,每次可将权值为正的一段区间全部变成 。设操作 时其权值为 。
性质 1:对于 ,。
证明:设 ,则 向左或向右扩展 一定不会使得 。
性质 1 的推论:
- 设操作 时区间内原有 的数量为 ,消去 的数量为 ,若 ,则 ,进一步有 。
性质 2:若存在方案使得 全为 ,则 。
证明:若初始存在 且 ,即存在 或 作为子串,则总存在操作序列,使得 ,易知 ,则 。
若初始不存在这样的 ,即无法操作长度大于 的区间,则无法对序列产生任何影响,即无解。
性质 3:对于任意 , 或 。
证明:
先证明 相交必然包含:考虑两次相交但不包含的操作 ,,其中 ,则令 一定合法,因为此时 均为 。
接下来只需证明 一定相交。我们使用调整法。
找到最后两个相邻且不交的区间 ,则 $I_{j + 1}\subset I_{j + 2} \subset \cdots \subset I_e$。因为 ,所以存在 使得 ,且 。
若 ,我们可以将 全部取消,替换为 的 “超区间” ,这样消去 的数量为 ,而 消去的 的数量显然不超过 ,所以替换后 的权值一定增加,而操作数不增。
若 ,类似地,取消操作 ,替换为在 后插入 的 “超区间” , 的权值增加,而操作数不变。
调整后,相邻且不交的区间的位置单调递减,因此调整总可以结束。
有了性质 2 和性质 3,我们可以设计 DP 表示 是否可行。转移暴力枚举扩展区间,单次检查时间复杂度 。
根据基本性质,对于每个 ,我们只关心最大的 使得 。因此,考虑 值域定义域互换 的套路,设 表示从 开始,最大的 使得原 等于 。
再根据基本性质,如果 且 ,那么 是无用的。因此,从 时,我们只关心所有不被包含的 区间,即左右端点单调递增。
设 表示操作 后相对于原序列,对包含 的区间的权值产生的贡献,即 。考虑检查 能否为 ,那么我们求出所有使得 的 的最大值 ,那么 加上 之后应为正数,即 。
这样,我们有了单次检查 的做法。结合递增性质,换成扫描线 + 单调栈 + 线段树二分即可 ,但总复杂度 及大常数仍无法通过。
我们进一步利用递增性质,从后往前扫描线。加入区间 时,权值小于 的区间就无用了。这启发我们使用单调队列,从队头到队尾 区间位置从右往左,同时 权值递减。我们希望找到队列中的第一个位置 ,使得存在 且 。预处理 表示使得 的最大的 ,则相当于检查是否有 。
显然具有单调性, 同样单调增( 递减),问题在于 。但是我们也有方法解决: 且 ,则 肯定不会作为左端点,丢掉所有这样的位置之后, 随着 递减而单调递增,故 递增,可使用单调队列维护。
看了下标算,定义是相同的,换了一种转移方式:求值的极值而不是求位置的极值。感觉麻烦了点。
总时间复杂度 。
#include <bits/stdc++.h> using namespace std; constexpr int N = 4e5 + 5; int n, k, a[N], b[N], s[N]; int f[N], tag[N], buc[N << 1]; bool check(int x) { for(int i = 1; i <= n; i++) { s[i] = s[i - 1] + (a[i] >= x ? 1 : -1); } memset(tag, 0, sizeof(tag)); for(int i = 1, lim = N; i <= n; i++) { if(s[i - 1] < lim) lim = s[i - 1], tag[i] = 1; } memset(buc, 0, n + 2 << 3); for(int i = 1; i <= n; i++) buc[n + s[i]] = i; for(int i = n * 2 - 1; ~i; i--) buc[i] = max(buc[i], buc[i + 1]); for(int i = 1; i <= n; i++) f[i] = i - 1; for(int _ = 1; _ <= k && (1 << _ - 1) <= n; _++) { static int d[N], val[N], hd, tl; hd = 1, tl = 0; for(int i = n; i; i--) { if(!tag[i]) continue; int v = f[i] - i + 1 - (s[f[i]] - s[i - 1]); while(hd <= tl && v >= val[tl]) tl--; val[++tl] = v, d[tl] = f[i]; while(hd <= tl) { int v = val[hd], p = buc[max(0, s[i - 1] + 1 - v + n)]; if(p >= d[hd]) {f[i] = p; break;} hd++; } } if(f[1] == n) return _ <= k; } return 0; } int main() { ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); cin >> n >> k; for(int i = 1; i <= n; i++) cin >> a[i], b[i] = a[i]; sort(b + 1, b + n + 1); int c = unique(b + 1, b + n + 1) - b - 1; int l = 1, r = c; while(l < r) { int m = l + r + 2 >> 1; if(check(b[m])) l = m; else r = m - 1; } cout << b[l] << "\n"; return 0; }
- 1
信息
- ID
- 8166
- 时间
- 2000ms
- 内存
- 512MiB
- 难度
- 7
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者