D-query SPOJ DQUERY

题意:询问你[l,r]范围内不同的数字有几个。
思路;这题查询是离线的,并且我们维护数组cnt,和结果ans(就是有几个不同的数字),cnt[i]=j,表示在当前的区间[L,R]中,第i个数字出现了j次。如果我们加上一个数字后cnt[i]是1,那么就说明这个数字是第一次出现,那就ans++,相反,如果我们删除一个数字i后,cnt[i]==0,那就说明该区间内不同的数字数量-1,即ans–;

#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
using namespace std;
const int maxn = 3e4 + 5;
int block, n, q;
int a[maxn], belog[maxn], cnt[1000005], ans;
//a存储数据,belog[i]=j,表示第i个数字属于第j块,cnt存储一个数字出现了几次,ans是有几个不同的数字
struct node {
    int l, r, id, val;//l r是区间的范围,id 便于最后输出,val是该组查询的答案
} e[200005];
bool cmp1(node a, node b) {
    return belog[a.l] < belog[b.l] || (belog[a.l] == belog[b.l] && a.r < b.r);
}
bool cmp2(node a, node b) {
    return a.id < b.id;
}
void updata(int x, int add) {
    if(add == 1) {
        cnt[a[x]]++;
        if(cnt[a[x]] == 1) ans++;//如果加上该数字i后cnt[i]是1 ,就说明i是该区间第一次出现
    } else if(add == -1) {//删除也同理
        cnt[a[x]]--;
        if(cnt[a[x]] == 0) ans--;
    }
}
int main() {
    scanf("%d", &n);
    for(int i = 1; i <= n; i++) {
        scanf("%d", &a[i]);
    }
    scanf("%d", &q);
    for(int i = 1; i <= q; i++) {
        scanf("%d%d", &e[i].l, &e[i].r);
        e[i].id = i;
    }
    block = sqrt(n);
    for(int i = 1; i <= n; i++) {//分块
        belog[i] = (i - 1) / block + 1;
    }
    sort(e + 1, e + 1 + q, cmp1);//排序
    cnt[a[1]]++;//初始化
    ans = 1;
    for(int i = 1, l = 1, r = 1; i <= q; i++) {//进行q次离线查询
        for(; r < e[i].r; r++) {
            updata(r + 1, 1);
        }
        for(; r > e[i].r; r--) {
            updata(r, -1);
        }
        for(; l > e[i].l; l--) {
            updata(l - 1, 1);
        }
        for(; l < e[i].l; l++) {
            updata(l, -1);
        }
        e[i].val = ans;
    }
    sort(e + 1, e + 1 + q, cmp2);
    for(int i = 1; i <= q; i++) {
        printf("%d\n", e[i].val);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/yiqzq/article/details/80673344