AT_jsc2019_final_h Distinct Integers 题解 线段树单侧递归

题目链接:https://www.luogu.com.cn/problem/AT_jsc2019_final_h

解题思路:完全来自 ikusiad大佬的博客

额外提一嘴:

如果我们用 \(p_i\) 表示 \(a_i\) 前面和 \(a_i\) 最接近的那个等于 \(a_i\) 的元素的下标(如果 \(a_i\) 前面没有等于 \(a_i\) 的数,则令 \(pre_i = -1\))。

则,我们可以定义一个 \(p_i = \max\limits_{0 \le j \le i} pre_j\)

则对于每次询问的区间 \([l, r)\),答案为

\[\sum_{i = l}^{r-1} i - p_i \]

示例程序:

还在debug,还没AC,AC了再贴……

2026/4/21 更新:线段树没写出来,and 暂时不想写了,于是拿分块过了这道题,时间复杂度 \(O(m \sqrt{n} \log n)\)

示例程序:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 5e5 + 5, maxb = sqrt(maxn) + 5;

int n, blo, Q, a[maxn], pre[maxn], PRE[maxn];
set<int> st[maxn];
long long sum[maxb], ss[maxn];

int id(int x) {
    return (x - 1) / blo + 1;
}

// 更新第 p 个分块
void update(int p) {
    int l = (p - 1) * blo + 1, r = min(p * blo, n);
    sum[p] = PRE[l] = ss[l] = pre[l];
    for (int i = l+1; i <= r; i++) {
        PRE[i] = max(PRE[i-1], pre[i]);
        sum[p] += PRE[i];
        ss[i] = ss[i-1] + PRE[i];
    }
}

// 将 pre[x] 修改为 y
void update(int x, int y) {
//    printf("update pre[%d] = %d\n", x, y);
    pre[x] = y;
    update(id(x));
}

long long query(int l, int r) {
    long long ans = 0;
    int x = l-1;
    if (id(l) + 1 >= id(r)) {
        for (int i = l; i <= r; i++) {
            x = max(x, pre[i]);
//            printf("[*] + %d\n", x);
            ans += x;
        }
        return ans;
    }
    for (int i = l; i <= id(l)*blo; i++) {
        x = max(x, pre[i]);
//        printf("[1] + %d\n", x);
        ans += x;
    }
    for (int i = id(l)+1; i < id(r); i++) {
        if (x >= PRE[i*blo]) {
            ans += 1ll * x * blo;
//            printf("[2.1] + %d * %d\n", x, blo);
        }
        else {
            int p = upper_bound(PRE+(i-1)*blo+1, PRE+i*blo+1, x) - PRE;
            assert(p >= (i-1)*blo+1);
            int len = p - ((i-1)*blo+1);
//            printf("[2.2] + %d * %d + %lld - %lld \n", x, len, ss[i*blo], ss[p-1]);
            if (len > 0)
                ans += 1ll * x * len + ss[i*blo] - ss[p-1];
            else
                ans += ss[i*blo] - ss[p] + PRE[p];
            x = PRE[i*blo];
        }
    }
    for (int i = (id(r)-1)*blo+1; i <= r; i++) {
        x = max(x, pre[i]);
//        printf("[3] + %d\n", x);
        ans += x;
    }
    return ans;
}

int main() {
    scanf("%d%d", &n, &Q);
    blo = sqrt(n);
    for (int i = 0; i < n; i++) {
        st[i].insert(0);
    }
    for (int i = 1; i <= n; i++) {
        scanf("%d", a+i);
        auto it = st[ a[i] ].insert(i).first;
        it--;
        pre[i] = *it;
    }
    for (int i = 0; i < n; i++) {
        st[i].insert(n+1);
    }
    for (int i = 1; i <= id(n); i++)
        update(i);
    while (Q--) {
        int op, x, y;
        scanf("%d%d%d", &op, &x, &y);
        if (op == 0) {
            x++;
            auto it1 = st[ a[x] ].lower_bound(x), it2 = it1;
            it1--, it2++;
            int l = *it1, r = *it2;
            if (r <= n) {
                update(r, l);
            }
            st[ a[x] ].erase(x);

            a[x] = y;
            it1 = st[ a[x] ].insert(x).first, it2 = it1;
            it1--, it2++;
            l = *it1, r = *it2;
            update(x, l);
            if (r <= n) {
                update(r, x);
            }
        }
        else {  // op == 1
            x++;
//            cout << "x = " << x << " , y = " << y << endl;
            long long ans = 1ll * (x + y) * (y - x + 1) / 2;
            ans -= query(x, y);
            printf("%lld\n", ans);
        }
    }
    return 0;
}
/**
5 5
0 1 2 1 4
0 3 3
0 2 4
1 2 5
*/
posted @ 2026-04-16 16:16  quanjun  阅读(15)  评论(0)    收藏  举报