拥挤的甜蜜小屋

0.题意简述

多组询问,每个询问求 c a r d ( { a [ i ] ∣ i ∈ [ l , r ] } ) card (\{a[i] | i \in [l, r]\}) card({a[i]i[l,r]})

1.前言

妙啊
第一眼看过去,马上想到莫队,正想嘲讽出题人,结果一看数据范围 n , q ≤ 1 0 6 n, q \leq 10^6 n,q106,马上就傻眼了。

2.

莫队时间复杂度起飞了,只得换种算法。由于 ta 是与权值挂钩,所以我们自然想到从权值入手(权值线段树),如果遇到两个相同元素怎么办?选下标靠前的还是下标靠后的?

如果我们的询问区间按照右端点递增排序,则我们一定选择下标靠后的(不超过区间右端点),因为下标靠前的在区间内,则下标靠后的在区间内。

所以我们离线记录询问,按照询问区间右端点递增排序,用权值线段树(树状数组同理)记录下对于每一个值 x x x 最后出现的下标 i i i,统计答案时用下标小于等于 r r r 的个数剪去下标小于等于 l − 1 l - 1 l1 的个数即可。

这里用的是树状数组(因为短)

#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;

template <typename T>
void read (T &x) {
    x = 0; T f = 1;
    char ch = getchar ();
    while (ch < '0' || ch > '9') {
        if (ch == '-') f = -1;
        ch = getchar ();
    }
    while (ch >= '0' && ch <= '9') {
        x = (x << 3) + (x << 1) + ch - '0';
        ch = getchar ();
    }
    x *= f;
}
template <typename T>
void write (T x) {
    if (x < 0) {
        x = -x;
        putchar ('-');
    }
    if (x < 10) {
        putchar (x + '0');
        return;
    }
    write (x / 10);
    putchar (x % 10 + '0');
}
template <typename T>
void print (T x, char ch) {
    write (x); putchar (ch);
}

const int Maxn = 1e6;
const int Maxa = 4 * 1e3;

int n, m;
int a[Maxn + 5], last[Maxa + 5], ans[Maxn + 5];

struct sec {
    int l, r, id;
}b[Maxn + 5];
bool cmp (sec x, sec y) {
    return x.r < y.r;
}

struct Array {
    int BIT[Maxn + 5];
    int lowbit (int x) { return x & -x; }
    void Update (int Index, int x) {
        Index++;
        for (int i = Index; i <= Maxn; i += lowbit (i)) {
            BIT[i] += x;
        }
    }
    int Query (int Index) {
        Index++;
        int res = 0;
        for (int i = Index; i >= 1; i -= lowbit (i)) {
            res += BIT[i];
        }
        return res;
    }
}tr;

int main () {
    read (n); read (m);
    for (int i = 1; i <= n; i++)
        read (a[i]);
    for (int i = 1; i <= m; i++)
        read (b[i].l), read (b[i].r), b[i].id = i;
    sort (b + 1, b + 1 + m, cmp);
    int poi = 1;
    for (int i = 1; i <= n; i++) {
        if (last[a[i]] != 0) 
            tr.Update (last[a[i]], -1);
        last[a[i]] = i; 
        tr.Update (last[a[i]], 1);
        while (poi <= m && b[poi].r == i) {
            ans[b[poi].id] = tr.Query (i) - tr.Query (b[poi].l - 1);
            poi++;
        }
    }
    for (int i = 1; i <= m; i++) {
        print (ans[i], '\n');
    }
    return 0;
}
posted @ 2021-06-13 21:02  C2022lihan  阅读(28)  评论(0编辑  收藏  举报