「树状数组」[SDOI2009]HH的项链

[SDOI2009]HH的项链

原题链接 [SDOI2009]HH的项链

题目大意

给你 (n) 个数,再给你 (q) 次询问,每次询问给你 (l, r) ,问你 (l, r) 中有多少个不同的数

题目题解

分析这道题我们发现,对于一个 ([L_1, R_1]) 存在另一个 ([L_2, R_1])(L_2) 严格大于 (L_1),那么就一定存在第一个区间不同的数 大于等于 第二个区间的不同的数,这里很显然有一种等于的情况,什么情况等于?在([L_2,R_1]) 中包含的数与 ([L_1, L_2 - 1]) 中包含的数 ,后者的数前者都有。那么我们就发现 对于这样的情况([L_1, L_2 - 1]) 中就没有必要存在了。但注意的是,这里我们是严格定义的 (R_1)相同 且(L_1 < L_2),我们继续推发现(R_2 > R_1) 也是符合条件的。

于是我们得到若我们按查询的右区间从小到大排序,这样的就能转化为一个线性的前缀和问题,我们用数据结构来维护每一位上是否为1或0,若为1则说明之后没出现相同的数,若为1则说明出现过,每次处理到查询位的(R),然后用数据结构查询 (sum_R - sum_{l - 1}) 的值,就能得到我们的答案了

我们这里用树状数组维护,代码如下

//#define fre yes

#include <cstdio>
#include <algorithm>

const int N = 1000005;
struct Node {
    int l, r;
    int pos;
} w[N];
int Vis[N], arr[N], ans[N];
int tree[N];

bool cmp(Node x, Node y) {
    return x.r < y.r;
}

int lowbit(int x) {
    return x & (-x);
}

int n;
void change(int x, int k) {
    while(x <= n) {
        tree[x] += k;
        x += lowbit(x);
    }
}

int sum(int x) {
    int res = 0;
    while(x > 0) {
        res += tree[x];
        x -= lowbit(x);
    } return res;
}

int main() {
    static int q;
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        scanf("%d", &arr[i]);
    }

    scanf("%d", &q);
    for (int i = 1; i <= q; i++) {
        int l, r;
        scanf("%d %d", &l, &r);
        w[i].l = l; w[i].r = r;
        w[i].pos = i;
    }

    std::sort(w + 1, w + 1 + q, cmp);

    int tot = 1;
    for (int i = 1; i <= q; i++) {
        for (int j = tot; j <= w[i].r; j++) {
            if(Vis[arr[j]]) change(Vis[arr[j]], -1);
            change(j, 1);
            Vis[arr[j]] = j;
        }

        tot = w[i].r + 1;
        ans[w[i].pos] = sum(w[i].r) - sum(w[i].l - 1);
    }

    for (int i = 1; i <= q; i++) {
        printf("%d
", ans[i]);
    } return 0;
}
原文地址:https://www.cnblogs.com/Nicoppa/p/11479310.html