题解:Luogu P14379 【MX-S9-T2】「LAOI-16」摩天大楼
题意
给定长度为 \(n\) 的序列 \(a\)。有 \(q\) 次修改,第 \(i\) 次修改给出 \(x_i,v_i\),表示令 \(a_{x_i}\gets v_i\)。每次修改后你需要求出
这里 \(\operatorname{mex}\) 运算的全集为 \(\mathbb{N}^{+}\)。\(1\leq n,q,a_i,v_i\leq 10^6\)。
题解
简单题。
正难则反,考虑求出有多少区间 \([i,j]\) 满足 \(\forall i\leq k<j,\operatorname{mex}\{a_{i\sim k}\}= \operatorname{mex}\{a_{k+1\sim j}\}\)。由于 \(\operatorname{mex}\{a_{i\sim k}\}\) 关于 \(k\) 单调不减,\(\operatorname{mex}\{a_{k+1\sim j}\}\) 关于 \(k\) 单调不增,所以上述条件等价于所有 \(\operatorname{mex}\{a_{i\sim k}\}\) 和 \(\operatorname{mex}\{a_{k+1\sim j}\}\) 相等。此时 \(\operatorname{mex}\{a_i\}=\operatorname{mex}\{a_j\}\),分类讨论 \(a_i,a_j\) 的取值:
- \(a_i=a_j=1\):即 \(\operatorname{mex}\{a_i\}=\operatorname{mex}\{a_j\}=2\),因此此时 \(\forall i<k<j,a_k\neq 2\)。
- \(a_i\neq 1,a_j\neq 1\):即 \(\operatorname{mex}\{a_i\}=\operatorname{mex}\{a_j\}=1\),因此此时 \(\forall i<k<j,a_k\neq 1\)。
对于第一种区间,枚举相邻的两个 \(2\),设它们之间有 \(c\) 个 \(1\),则贡献为 \(\dfrac{c(c-1)}{2}\)。对于第二种区间,枚举相邻的两个 \(1\),设它们之间有 \(d\) 个数,则贡献为 \(\dfrac{d(d-1)}{2}\)。
考虑动态维护上面的两类式子。容易想到开两个 set 分别维护 \(a\) 中所有 \(1\) 和所有 \(2\) 的位置,每次修改看作删除某个数,再插入某个数。插入/删除 \(2\) 时,\(\sum\dfrac{c(c-1)}{2}\) 是好维护的,用一棵 BIT 支持动态区间查询 \(1\) 个数即可。插入/删除 \(1\) 时,\(\sum\dfrac{d(d-1)}{2}\) 同样是好维护的,然后还要找到这个 \(1\) 右边的第一个 \(2\) 更新 \(\sum\dfrac{c(c-1)}{2}\)。这样就做完了,时间复杂度为 \(\mathcal{O}(n\log{n})\)。
代码
#include <bits/stdc++.h>
using namespace std;
#define lowbit(x) ((x) & -(x))
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
typedef pair<int, int> pii;
const int N = 1e6 + 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, q, a[N];
ll sum1, sum2;
set<int> s1, s2;
struct BIT {
int c[N];
inline int query(int x) {
int res = 0;
for (; x; x -= lowbit(x)) res += c[x];
return res;
}
inline int query(int l, int r) { return query(r) - query(l - 1); }
inline void add(int x, int v) { for (; x <= n; x += lowbit(x)) c[x] += v; }
} ft;
ll calc(int x) { return (ll)x * (x - 1) >> 1; }
void upd1(int x, bool tp) {
int d = tp ? 1 : -1;
auto nxt = s2.upper_bound(x), prv = prev(nxt);
int cnt1 = ft.query(*prv + 1, *nxt - 1), cnt2 = cnt1 + d;
ft.add(x, d);
sum2 += calc(cnt2) - calc(cnt1);
prv = prev(s1.lower_bound(x)), nxt = s1.upper_bound(x);
sum1 += (calc(x - *prv - 1) + calc(*nxt - x - 1) - calc(*nxt - *prv - 1)) * d;
if (tp) s1.insert(x); else s1.erase(x);
}
void upd2(int x, bool tp, bool flag = 0) {
int d = tp ? 1 : -1;
auto prv = prev(s2.lower_bound(x)), nxt = s2.upper_bound(x);
sum2 += (calc(ft.query(*prv + 1, x - 1)) + calc(ft.query(x + 1, *nxt - 1)) - calc(ft.query(*prv + 1, *nxt - 1))) * d;
if (tp) s2.insert(x); else s2.erase(x);
}
int main() {
ios::sync_with_stdio(0), cin.tie(0);
cin >> n >> q;
s1.insert(0), s2.insert(0);
for (int i = 1; i <= n; ++i) {
cin >> a[i];
if (a[i] == 1) {
ft.add(i, 1);
int prv = *prev(s1.end());
sum1 += calc(i - prv - 1), s1.insert(i);
} else if (a[i] == 2) {
int prv = *prev(s2.end());
sum2 += calc(ft.query(prv + 1, i - 1)), s2.insert(i);
}
}
int prv = *prev(s1.end()); sum1 += calc(n - prv), s1.insert(n + 1);
prv = *prev(s2.end()), sum2 += calc(ft.query(prv + 1, n)), s2.insert(n + 1);
while (q--) {
int x, v; cin >> x >> v;
if (a[x] == 1) upd1(x, 0);
else if (a[x] == 2) upd2(x, 0);
a[x] = v;
if (v == 1) upd1(x, 1);
else if (v == 2) upd2(x, 1);
cout << calc(n) - sum1 - sum2 << '\n';
}
return 0;
}

浙公网安备 33010602011771号