拥挤的甜蜜小屋

0.题意简述

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

1.前言

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

2.

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

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

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

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

#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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值