We need to count subarrays containing exactly one incomplete color.
Recall that a color is incomplete if the subarray contains at least one, but not all of its occurrences in the original array.
A straightforward solution would enumerate every subarray and maintain the number of incomplete colors. This takes O(n^2) time, which is too slow.
Instead, let's fix the right endpoint and count all valid left endpoints .
For a fixed right endpoint , let be the number of incomplete colors in the subarray [l,r].
Our goal is to count positions for which
We will maintain these values while moving from left to right.
For each color , calculate:
first_c — the position of its first occurrence.
last_c — the position of its last occurrence.
prev_c — the position of its latest occurrence before the current position, initially .
Suppose we extend the current subarray by adding a_r=c.
Consider all left endpoints satisfying
For these subarrays, color was previously absent and has now appeared.
Therefore, we can temporarily consider it incomplete and increase by one on the interval
However, if r=last_c, we have just reached the final occurrence of this color.
For every l\leq first_c, the subarray now contains all occurrences of . Thus, this color is no longer incomplete.
We must decrease by one on
Notice that these two updates also handle colors appearing only once: the updates simply cancel each other.
So, for every position , we perform at most two range additions.
We need a data structure supporting:
Adding or to all values on an interval.
Counting how many values are equal to .
We can use a segment tree with lazy propagation.
For every node, maintain:
— the minimum value in the segment.
— the number of occurrences of this minimum.
— the number of values equal to mn+1.
— the pending addition.
When we add a constant to the entire segment, only and change. The values of and remain unchanged.
We can also merge two child nodes in constant time.
The minimum of the parent is the smaller minimum of its children.
To calculate , we sum the minimum counts of children whose minimum equals the parent's minimum.
To calculate , a child contributes:
Its , if its minimum equals the parent's minimum.
Its , if its minimum is exactly one greater than the parent's minimum.
Nothing otherwise.
Thus, every range addition takes O(\log n) time.
Initially, all values are zero.
After processing position , we only modify positions l\leq r. The remaining positions still have value zero and do not contribute to the answer.
Moreover, after processing the entire array, f(1)=0, because the whole array contains all occurrences of every color.
Therefore, the global minimum always equals zero.
This means that at the root of the segment tree is exactly the number of subarrays ending at that contain one incomplete color.
We add this value to the answer after every position.
We process all positions, performing at most two segment tree updates for each position.
Each update takes O(\log n) time.
Therefore, the total time complexity is O(n\log n).
The segment tree and auxiliary arrays require additional memory.
The answer can be as large as O(n^2), so we use a 64-bit integer.
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5 + 7;
int a[N], fst[N], lst[N], prv[N];
int mn[4 * N], cnt[4 * N], cnt1[4 * N], lazy[4 * N];
void build(int v, int l, int r) {
cnt[v] = r - l + 1;
if (l == r) return;
int mid = (l + r) / 2;
build(v * 2, l, mid);
build(v * 2 + 1, mid + 1, r);
}
void pull(int v) {
int x = v * 2, y = v * 2 + 1;
mn[v] = min(mn[x], mn[y]);
cnt[v] = cnt1[v] = 0;
if (mn[x] == mn[v]) {
cnt[v] += cnt[x];
cnt1[v] += cnt1[x];
} else if (mn[x] == mn[v] + 1) {
cnt1[v] += cnt[x];
}
if (mn[y] == mn[v]) {
cnt[v] += cnt[y];
cnt1[v] += cnt1[y];
} else if (mn[y] == mn[v] + 1) {
cnt1[v] += cnt[y];
}
}
void push(int v) {
if (!lazy[v]) return;
int x = v * 2, y = v * 2 + 1;
mn[x] += lazy[v];
mn[y] += lazy[v];
lazy[x] += lazy[v];
lazy[y] += lazy[v];
lazy[v] = 0;
}
void upd(int v, int l, int r, int ql, int qr, int val) {
if (ql > qr) return;
if (ql <= l && r <= qr) {
mn[v] += val;
lazy[v] += val;
return;
}
push(v);
int mid = (l + r) / 2;
if (ql <= mid)
upd(v * 2, l, mid, ql, qr, val);
if (qr > mid)
upd(v * 2 + 1, mid + 1, r, ql, qr, val);
pull(v);
}
void solve() {
int n;
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i];
if (!fst[a[i]]) fst[a[i]] = i;
lst[a[i]] = i;
}
build(1, 1, n);
ll ans = 0;
for (int r = 1; r <= n; r++) {
int c = a[r];
upd(1, 1, n, prv[c] + 1, r, 1);
if (r == lst[c]) {
upd(1, 1, n, 1, fst[c], -1);
}
prv[c] = r;
ans += cnt1[1];
}
cout << ans << '\n';
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0);
solve();
return 0;
}