区间内不同数的个数

题目链接

询问区间内不同数的个数

做法一:树状数组做法:离线处理,将以r从小到大排序,一个标记数组记录数出现的位置,树状数组里只记录当前所有相同的数的最右位置,这样一个数就只会被加一次。
在这里插入图片描述

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 7;
#define lowbit(i) i&(-i)
int sum[maxn];
int n, m;
void add(int x, int C) {
    for (int i = x; i <= n; i += lowbit(i))
        sum[i] += C;
}

int query(int x) {
    int ans = 0;
    for (int i = x; i; i -= lowbit(i))
        ans += sum[i];
    return ans;
}
int a[30005], vis[maxn];
struct node {
    int l, r, id;
}t[maxn];
bool cmp(node x, node y) {
    return x.r < y.r;
}
int ans[maxn];
int main()
{
    cin >> n;
    for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
    cin >> m;
    for (int i = 1; i <= m; i++) {
        scanf("%d %d", &t[i].l, &t[i].r);
        t[i].id = i;
    }
    sort(t + 1, t + 1 + m, cmp);
    int pos = 1;
    for (int i = 1; i <= m; i++) {
        while (pos <= n && pos <= t[i].r) {
            if(vis[a[pos]]) {
                add(vis[a[pos]], -1);
            }
            add(pos, 1);
            vis[a[pos]] = pos;
            pos++;
        }
        ans[t[i].id] = query(t[i].r)- query(t[i].l - 1);
    }
    for (int i = 1; i <= m; i++) printf("%d\n", ans[i]);
}

做法二:主席树在线处理,主席树也是维护当前数的最右位置,再插入数据的时候,如果这个数没有出现过,就直接插入计数;如果之前出现过,则将之前的位置-1,消除前面数的影响,当前位置加一。
在这里插入图片描述

#include <bits/stdc++.h>
using namespace std;
const int maxn = 3e4 + 7;
typedef long long ll;
int root[maxn], a[maxn], cnt, n, m;
struct node {
    int l, r, sum;
}T[maxn * 20];
void update(int l, int r, int &x, int y, int pos, int C) {
    T[++cnt] = T[y];
    x = cnt;
    T[cnt].sum += C;
    if(l == r) return ;
    int mid = (l + r) >> 1;
    if(pos <= mid) update(l, mid, T[x].l, T[y].l, pos, C);
    else update(mid + 1, r, T[x].r, T[y].r, pos, C);
}
int query(int id, int l, int r, int pos) {//查询的是l,r区间的和,存在的数是1,不存在的是0,所以和就是不同数的个数
    if(l == r) return T[id].sum;
    int mid = (l + r) >> 1;//如果l的位置在左边则返回右子树的和加上剩下左子树的和
    if(pos <= mid) return T[T[id].r].sum + query(T[id].l, l, mid, pos);
    else return query(T[id].r, mid + 1, r, pos);
}
int vis[1000005];
int main()
{
    cin >> n;
    for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
    for (int i = 1; i <= n; i++) {
        if(!vis[a[i]]) update(1, n, root[i], root[i - 1], i, 1);
        else {//这个数出现过则把之前位置减一,当前位置加一
            update(1, n, root[i], root[i - 1], vis[a[i]], -1);
            update(1, n, root[i], root[i], i, 1);
        }
        vis[a[i]] = i;
    }
    scanf("%d", &m);
    while (m--) {
        int l, r;
        scanf("%d%d", &l, &r);
        printf("%d\n", query(root[r], 1, n, l));//从r查询是因为root[r]存的是l,r区间最新的位置信息
    }
}

发布了26 篇原创文章 · 获赞 2 · 访问量 420

猜你喜欢

转载自blog.csdn.net/D_Bamboo_/article/details/103495267
今日推荐