题意
给定长度为 \(n\) 的序列 \(a\) 和值域 \(V\)。有 \(m\) 次操作:
- 给定 \(l,r,x\),将 \(a[l,r]\) 中 \(=x\) 的数改为 \(0\)。
- 给定 \(x\),在序列末尾添加 \(x\)。
- 给定 \(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;
}