P1972 [SDOI2009] HH的项链

本文介绍了一种使用树状数组解决区间内不同元素计数问题的方法。通过动态更新和离线查询,有效地计算出指定区间内的不同贝壳种类数量。
题意:

给你你一个长度为n的数组,然后数组中不同的数字表示不同种类的贝壳,然后给你m中询问,每次询问一个区间内有多少种贝壳。

思路:

首先题目中说了“某一段贝壳中,包含了多少种不同的贝壳?”,意思是,我们在找一段区间的贝壳的时候,如果同种贝壳出现了多次,那么我们只考虑一种,最开始的时候我就只用了一个简单的树状数组模板,因为我只考虑了第一次出现的位置,而忽略了对后面区间的影响。回到这个题,假如给你了的数组是:1、2、3、4、1、5、6、4,那么问你1-5之间的贝壳种类数,那么是4,如果问你2-6之间的贝壳数,也是4,所以我们在查找的时候只需要考虑在这个区间里面是否出现过这个种类的贝壳,我们需要动态的更新,离线的查询。
那么现在给了你查询的区间,对于查询的区间我们怎么处理呢?我们上面说了需要动态的更新,我们是不是在当前查询的区间内,需要看当前位置的这个数是否出现过,如果出现过,那么我们直接将这个数上次出现的位置去掉就可以了,那么我们需要一个数组来保存每个数上一次出现的位置,将上次出现的位置去掉,在新的位置打上标记。另外,我们还需要对查询区间进行操作,我们需要将查询区间按照r进行排序,我们要保证这一段区间的修改对后面的区间不会产生影响,(还有就是我们建树是从左往右的)。最后我们用一个数组来表示记录每一次查询的结果就可以了。

#include <bits/stdc++.h>

using namespace std;

const int N = 1e6 + 10;

int n, m;
int a[N];
int tr[N];
int vis[N];
int ans[N];

struct node
{
    int l, r;
    int pos;
    bool operator < (const node &t) const
    {
        return r < t.r;
    }
}op[N];

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

void add(int x, int k) { for (int i = x; i <= n; i += lowbit(i)) tr[i] += k; }

int sum(int x)
{
    int sum = 0;
    for (int i = x; i; i -= lowbit(i)) sum += tr[i];
    return sum;
}

signed main()
{
    scanf("%d", &n);
    for (int i = 1; i <= n; i ++) scanf("%d", &a[i]);

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

    int nex = 1;
    for (int i = 1; i <= m; i ++)
    {
        for (int j = nex; j <= op[i].r; j ++)
        {
            if (vis[a[j]]) add(vis[a[j]], -1);
            add(j, 1);
            vis[a[j]] = j;
        }

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

    for (int i = 1; i <= m; i ++) printf("%d\n", ans[i]);

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值