题目大意:
给你n个元素的颜色。
问元素的颜色互不相同的区间有多少个。
支持单点修改。
1<=n<=10^5
题解:
设last[i]表示i元素的颜色上一次出现的位置。
假设我们确定了区间左端点x,
右端点y要满足条件则
max(last[i])<x,(i in [x..y])
再设一个p数组,p[last[i]]=i
则 右端点<min(p[i])(i>=x)
满足条件的y就有min(p[i])(i>=x)-x个。
这相当于倒着扫,min值的和。
可以线段树套splay维护。
也可以顺着做递增的单调栈来维护。
Code:
#include<set>
#include<cstdio>
#define ll long long
#define min(a, b) ((a) < (b) ? (a) : (b))
#define fo(i, x, y) for(int i = x; i <= y; i ++)
using namespace std;
const int N = 1e5 + 5, M = N * 30;
int n, m, x, y, a[N], la[N];
set<int> s[N];
int find(int a, int i) {
if(s[a].empty() || (*s[a].begin()) >= i) return 0;
if(*s[a].rbegin() < i) return *s[a].rbegin();
return *(-- s[a].lower_bound(i));
}
int p[N], t[N * 4]; ll fx[N * 4];
ll query(int i, int x, int y, int q) {
if(x == y) return min(p[x], q);
int m = x + y >> 1;
return t[i + i + 1] >= q ? (query(i + i, x, m, q) + (ll)(y - m) * q) : (fx[i] + query(i + i + 1, m + 1, y, q));
}
void chan(int i, int x, int y, int l) {
if(x == y) {t[i] = p[x], fx[i] = p[x]; return;}
int m = x + y >> 1;
if(l <= m) chan(i + i, x, m, l); else chan(i + i + 1, m + 1, y, l);
t[i] = min(t[i + i], t[i + i + 1]);
fx[i] = query(i + i, x, m, t[i + i + 1]);
}
void GG(int x, int y) {
la[x] = y; p[y] = x;
if(y) chan(1, 1, n, y);
}
int main() {
scanf("%d", &n);
fo(i, 1, n) p[i] = n + 1, chan(1, 1, n, i);
fo(i, 1, n) {
scanf("%d", &a[i]), s[a[i]].insert(i);
GG(i, find(a[i], i));
}
for(scanf("%d", &m); m; m --) {
scanf("%d", &x);
if(x == 0) {
printf("%lld\n", query(1, 1, n, 2e9) - (ll)n * (n + 1) / 2);
} else {
scanf("%d %d", &x, &y);
int l = *s[a[x]].begin(), r = *s[a[x]].rbegin();
if(r > x) {
r = *s[a[x]].upper_bound(x);
if(l < x) {
l = *(-- s[a[x]].lower_bound(x));
GG(r, l);
} else {
GG(r, 0);
}
} else {
if(la[x]) p[la[x]] = n + 1, chan(1, 1, n, la[x]);
}
s[a[x]].erase(x);
s[y].insert(x);
l = *s[y].begin(), r = *s[y].rbegin();
if(r > x) {
r = *s[y].upper_bound(x);
GG(r, x);
} else {
p[x] = n + 1; chan(1, 1, n, x);
}
if(l < x) {
l = *(-- s[y].lower_bound(x));
GG(x, l);
} else {
GG(x, 0);
}
a[x] = y;
}
}
}