题解:P14379 【MX-S9-T2】「LAOI-16」摩天大楼
DanielDingDang · · 题解
本题解中,重新定义
一次询问等价于求出
简化 f 的计算过程
这部分直接分讨即可。
- 如果
[l,r] 中没有出现1 ,f(l,r)=0 。 - 如果
[l,r] 中出现了1 ,设x 为任意满足x\in [l,r],a_x=1 的位置:- 若
a_l\neq 1 ,k 取x-1 即可,f(l,r)=1 。 - 若
a_r\neq 1 ,k 取x 即可,f(l,r)=1 。 - 若
a_l=a_r=1 : - 如果
[l+1,r-1] 中出现了2 ,则设2 的位置为p ,k 取p-1 即可,f(l,r)=1 。 - 如果
[l+1,r-1] 中没有出现2 ,f(l,r)=0 。
- 若
查询操作处理
注意到
根据上述分类讨论,我们只需要处理两种情况。
- 如果
[l,r] 中没有出现1 ,对于一个极长的不含有1 的长度为x 的连续子序列,其贡献显然为\displaystyle\frac{1}{2}x(x+1) 。 - 如果
[l,r] 中没有出现2 且a_l=a_r=1 ,则对于一个极长的不含有2 的连续子序列,若其中有x 个1 ,其贡献显然为\displaystyle \frac{1}{2}x(x+1) 。
可以简单的用两个 set 分别维护
代码写比较简洁。
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1000010;
int n, tr[N], T, a[N], res;
set<int> s[3];
int lb(int x) { return x & -x; }
void upd(int x, int d) { for (; x < N; x += lb(x)) tr[x] += d; }
int q(int x) { int res = 0; for (; x; x -= lb(x)) res += tr[x]; return res; }
int C(int x) { return x * (x - 1) / 2; }
int g(int l, int r, int op) {
if (op == 1) return r - l - 1;
return q(r) - q(l);
}
void upd(int x, int d, int num) {
if (num == 1) {
auto itr = s[2].lower_bound(x), itl = prev(itr);
res -= C(q(*itr) - q(*itl)); upd(x, d);
res += C(q(*itr) - q(*itl));
}
if (d == 1) s[num].insert(x);
auto it = s[num].find(x), itl = prev(it), itr = next(it);
res += d * C(g(*itl, *it, num)) + d * C(g(*it, *itr, num));
res -= d * C(g(*itl, *itr, num));
if (d == -1) s[num].erase(x);
}
signed main() {
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
cin >> n >> T;
for (int i = 1; i <= n; i ++ ) cin >> a[i];
for (int i = 1; i <= n; i ++ )
if (a[i] == 1) s[1].insert(i), upd(i, 1);
else if (a[i] == 2) s[2].insert(i);
for (int i = 1; i < 3; i ++ )
s[i].insert(0), s[i].insert(n + 1);
for (int num = 1, lst = 0; num < 3; num ++ , lst = 0)
for (int v : s[num])
if (v) res += C(g(lst, v, num)), lst = v;
while (T -- )
{
int x, d;
cin >> x >> d;
if (a[x] == d) { cout << C(n) - res << "\n"; continue; }
if (a[x] <= 2) upd(x, -1, a[x]);
if (d <= 2) upd(x, 1, d);
cout << C(n) - res << "\n", a[x] = d;
}
return 0;
}