C. Goodbye Souvenir
∑ i = L R i − p r e A i [ p r e A i ≥ L ] \sum\limits_{i = L} ^{R} i - pre_{A_i} [pre_{A_i} \geq L] i=L∑Ri−preAi[preAi≥L],进一步考虑即 ∑ i − p r e A i [ i ≤ R , p r e A i ≥ L ] \sum i - pre_{A_i}[i \leq R, pre_{A_i} \geq L] ∑i−preAi[i≤R,preAi≥L],
① i ≤ R i \leq R i≤R,
② p r e A i ≥ L pre_{A_i} \geq L preAi≥L
③ 时间 t t t维度
由此这个问题被转化为了一个标准的三维偏序问题,第一维为时间 t t t,第二维为 i i i,第三维为 p r e A i pre_{A_i} preAi,在第三维中动态插入 i − p r e A i i - pre_{A_i} i−preAi的值。
因此考虑如下一个结构体:
struct {
int op, x, y, f, id;
};
依次为:存放操作种类,存放 i i i,存放 p r e A i pre_{A_i} preAi,存放 i − p r e A i i - pre_{A_i} i−preAi,存放对第几个询问贡献答案。
考虑修改操作,对应 p r e [ p ] , p , n e x t [ p ] pre[p], p, next[p] pre[p],p,next[p],我们修改的是 p p p节点的信息,也就是这个节点不复存在了,
所以显然加入操作 { 1 , p , p r e [ p ] , p r e [ p ] − p , 0 } \{1, p, pre[p], pre[p] - p, 0\} {1,p,pre[p],pre[p]−p,0},也就是减去之前存在的 { 1 , p , p r e [ p ] , p − p r e [ p ] , 0 } \{1, p, pre[p], p - pre[p], 0\} {1,p,pre[p],p−pre[p],0},
考虑其对后继的影响,显然也要减去之前存在的后继信息,也就是加入 { 1 , n e x t [ p ] , p , p − n e x t [ p ] , 0 } \{1, next[p], p, p - next[p], 0\} {1,next[p],p,p−next[p],0},
然后加上修改后其后继的值 { 1 , n e x t [ p ] , p r e [ p ] , n e x t [ p ] − p r e [ p ] , 0 } \{1, next[p], pre[p], next[p] - pre[p], 0\} {1,next[p],pre[p],next[p]−pre[p],0}。
同样的对于修改后,也就是插入了一个新的值,我们也要先删去当前加入的点的后继,然后再加入当前点,以及后继的最新信息。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
int a[N], pre[N], n, m, top;
ll ans[N], tree[N];
set<int> st[N];
struct Res {
int op, x, y, f, id;
}Q[7 * N];
bool cmpx(Res a, Res b) {
return a.x < b.x;
}
bool cmpid(Res a, Res b) {
return a.id < b.id;
}
inline int lowbit(int x) {
return x & (-x);
}
void clear(int x) {
while (x) {
tree[x] = 0;
x -= lowbit(x);
}
}
void update(int x, int v) {
while (x) {
tree[x] += v;
x -= lowbit(x);
}
}
ll query(int x) {
ll res = 0;
while (x <= n) {
res += tree[x];
x += lowbit(x);
}
return res;
}
void CDQ(int l, int r) {
if (l == r) {
return ;
}
int mid = l + r >> 1;
CDQ(l, mid), CDQ(mid + 1, r);
for (int i = l, j = mid + 1; j <= r; j++) {
while (i <= mid && Q[i].x <= Q[j].x) {
if (Q[i].op & 1) {
update(Q[i].y, Q[i].f);
}
i++;
}
if (Q[j].op == 2) {
ans[Q[j].id] += query(Q[j].y);
}
}
for (int i = l; i <= mid; i++) {
clear(Q[i].y);
}
sort(Q + l, Q + r + 1, cmpx);
}
int main() {
// freopen("in.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
// ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
scanf("%d %d", &n, &m);
for (int i = 1; i <= n; i++) {
st[i].insert(0);
}
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
pre[i] = *--st[a[i]].end();
st[a[i]].insert(i);
Q[++top] = {1, i, pre[i], i - pre[i], 0};
}
for (int i = 1, op, x, y; i <= m; i++) {
scanf("%d %d %d", &op, &x, &y);
if (op & 1) {
auto it = st[a[x]].lower_bound(x);
Q[++top] = {1, x, pre[x], pre[x] - x, 0};
it++;
if (it != st[a[x]].end()) {
Q[++top] = {1, *it, pre[*it], pre[*it] - *it, 0};
pre[*it] = pre[x];
Q[++top] = {1, *it, pre[*it], *it - pre[*it], 0};
}
st[a[x]].erase(x);
a[x] = y;
st[a[x]].insert(x);
it = st[a[x]].lower_bound(x);
it--;
pre[x] = *it;
Q[++top] = {1, x, pre[x], x - pre[x], 0};
++it, ++it;
if (it != st[a[x]].end()) {
Q[++top] = {1, *it, pre[*it], pre[*it] - *it, 0};
pre[*it] = x;
Q[++top] = {1, *it, pre[*it], *it - pre[*it], 0};
}
}
else {
Q[++top] = {op, y, x, 0, i};
}
}
CDQ(1, top);
sort(Q + 1, Q + 1 + top, cmpid);
for (int i = 1; i <= top; i++) {
if (Q[i].op == 2) {
printf("%lld\n", ans[Q[i].id]);
}
}
return 0;
}