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

题解:Luogu P14175 【MX-X23-T5】向死存魏

题意

给定长度为 \(n\) 的序列 \(a\) 和值域 \(V\)。有 \(m\) 次操作:

  1. 给定 \(l,r,x\),将 \(a[l,r]\)\(=x\) 的数改为 \(0\)
  2. 给定 \(x\),在序列末尾添加 \(x\)
  3. 给定 \(l\),查询最小的 \(r\) 使得 \(a[l,r]\) 中包含了 \([1,V]\) 中的所有数,或报告无解。

\(1\leq n,m,V\leq 5\times 10^5\)\(1\leq x,a_i\leq V\)

题解

不是很难的题,击杀了。

下面视 \(n,m,V\) 同阶。

考虑对于每个值 \(x\),把序列中所有 \(=x\) 的下标取出来,记作 \(p_1,p_2,\cdots,p_k\),特别地,令 \(p_0=0\)。考察每个 \(1\leq i\leq k\),我们把 \([p_{i-1}+1,p_i]\) 视作一个权值为 \(p_i\) 的线段。显然每个点最多只会被一个值对应的线段覆盖一次,因此对于每次查询,我们首先判断这个点是否被覆盖了恰好 \(V\) 次,若不是则无解,否则答案就是所有覆盖这个点的线段中权值的最大值。

每个点的覆盖次数容易用 BIT 维护,对于权值的最大值,需要支持区间 \(\operatorname{chkmax}\) 和撤销某次 \(\operatorname{chkmax}\) 的效果,我们开一棵线段树,在每个节点上维护一个 multiset,这样每次 \(\operatorname{chkmax}\) 或撤销在对应节点的 multiset 上增删即可,单点查询就返回根到叶子的路径上的 multiset 的最大值。对于每种值,我们用 set 维护其下标序列,那么操作 \(1,2\) 就对应增删区间即可。显然操作 \(1\) 直接遍历区间内 \(=x\) 的位置均摊下来是 \(\mathcal{O}(n\log{n})\) 的。

这样我们就得到了一个 \(\mathcal{O}(n\log^2{n})\) 的做法。可以获得 \(60\) 分。

考虑优化,瓶颈在于线段树的部分。不难注意到对于每种值,线段的权值是从左到右单调递增的,所以对于每个线段 \([l,r]\),我们直接对 \([l,n]\) 做后缀 \(\operatorname{chkmin}\) 也是对的。而对于撤销,可以发现我们总是删除一段区间里的若干相邻线段 \([l_i,r_i]\),然后插入新线段 \([L,R]\),并且因为这些被删除的线段都被 \([L,R]\) 包含,所以我们直接把新线段的影响覆盖上去,就可以撤销掉被删除线段的影响了。这样只需要使用另一棵 BIT 支持后缀 \(\operatorname{chkmin}\) 和单点查询,时间复杂度就优化到了 \(\mathcal{O}(n\log{n})\)

代码

#include <bits/stdc++.h>using namespace std;#define lowbit(x) ((x) & -(x))
typedef long long ll;
typedef pair<int, int> pii;
const int N = 5e5 + 5;template<typename T> inline void chk_min(T &x, T y) { x = min(x, y); }
template<typename T> inline void chk_max(T &x, T y) { x = max(x, y); }int n, m, v, len, a[N];
set<int> pos[N];struct BIT {int c[N << 1];int query(int x) {int res = 0;for (; x; x -= lowbit(x)) res += c[x];return res;}void add(int x, int v) { for (; x <= n + m; x += lowbit(x)) c[x] += v;}
} ft;
struct BIT2 {int c[N << 1];int query(int x) {int res = 0;for (; x; x -= lowbit(x)) chk_max(res, c[x]);return res;}void upd(int x, int v) { for (; x <= n + m; x += lowbit(x)) chk_max(c[x], v); }
} ft2;int main() {ios::sync_with_stdio(0), cin.tie(0);cin >> n >> m >> v;for (int i = 1; i <= v; ++i) pos[i].insert(0);for (int i = 1; i <= n; ++i) {cin >> a[i];int prv = *prev(pos[a[i]].end());ft.add(prv + 1, 1), ft.add(i + 1, -1), ft2.upd(prv + 1, i);pos[a[i]].insert(i);}len = n;for (int i = 1; i <= m; ++i) {int tp; cin >> tp;if (tp == 1) {int l, r, x; cin >> l >> r >> x;auto it1 = pos[x].lower_bound(l), it2 = pos[x].upper_bound(r);for (auto it = it1; it != it2; ++it) {int L = (*prev(it)) + 1, R = *it;ft.add(L, -1), ft.add(R + 1, 1);}if (it2 != pos[x].end()) {int L = (*prev(it2)) + 1, R = *it2;ft.add(L, -1), ft.add(R + 1, 1);}pos[x].erase(it1, it2);it2 = pos[x].upper_bound(r);if (it2 != pos[x].end()) {int L = (*prev(it2)) + 1, R = *it2;ft.add(L, 1), ft.add(R + 1, -1), ft2.upd(L, R);}} else if (tp == 2) {int x; cin >> x;int prv = *prev(pos[x].end()); ++len;ft.add(prv + 1, 1), ft.add(len + 1, -1), ft2.upd(prv + 1, len); pos[x].insert(len);} else {int l, ans; cin >> l;if (ft.query(l) < v) ans = -1;else ans = ft2.query(l);cout << ans << endl;}}return 0;
}
http://www.hskmm.com/?act=detail&tid=35331

相关文章:

  • 题解:Luogu P14254 分割(divide)
  • 题解:Luogu P6898 [ICPC 2014 WF] Metal Processing Plant
  • 20251020
  • 32-腾讯IM接入资料和定价
  • 题解:AtCoder ARC207A Affinity for Artifacts
  • 构造单
  • [笔记]高斯消元
  • 半导体设备各细分领域的国内外龙头公司
  • CSP-S 34
  • 02.Python百行代码实现抽奖系统
  • CSP-S 35
  • CSP-S 29
  • 2025网络安全振兴杯wp
  • 10.20每日总结
  • CSP-S 31
  • 后缀树
  • ES原理、zookeeper、kafka
  • CF1606E Arena 题解(动态规划)
  • 服务器CPU市场概况2025
  • CSP-S 24
  • 读书笔记:深入理解java虚拟机
  • CSP-S 19
  • CSP-S 20
  • Flutter应用设置插件 - 轻松打开iOS和Android系统设置
  • CSP-S 22
  • Project. 2025.11化学小组pre
  • MySQLDay1
  • 蛋白表达标签:重组蛋白研究的精妙引擎
  • 106.腾讯地图位置服务再出错
  • 心理咨询系统