Incomplete Color#13095

Idea

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 .

Key Observation

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

f(l)=1.

We will maintain these values while moving from left to right.

For each color , calculate:

Suppose we extend the current subarray by adding a_r=c.

Consider all left endpoints satisfying

prev_c<l\leq r.

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

[prev_c+1,r].

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

[1,first_c].

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.

Solution

We need a data structure supporting:

We can use a segment tree with lazy propagation.

For every node, maintain:

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:

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.

Complexity Analysis

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.

Implementation

#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;
}