牛客网暑期ACM多校训练营(第一场) Different Integers(树状数组+ 离线)

链接:https://www.nowcoder.com/acm/contest/139/J

题意:

询问区间[l,r]外有多少不同的数字

思路:

开始不会做,后来学了树状数组,发现可以写了,因为是求两个区间的不同数值,两个区间的话不好做,可以把数组拼接在一起,这样区间[l,r]就变成[r,n+l]了,在用树状数组记录前i个数的不同的数

//
//  main.cpp
//  Different Integers
//
//  Created by dhl on 2018/7/23.
//  Copyright © 2018年 dhl. All rights reserved.
//

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
const int maxn = 2e5 + 5;
int c[maxn], a[maxn], ans[maxn], pre[maxn], mp[maxn];
struct ac{
    int l, r, id;
    bool operator < (const ac &a ) const {
        return r < a.r;
    }//按结尾r以升序排序
}p[maxn];

void updata(int x, int v) {
    while(x <= maxn) {
        c[x] += v;//c[x]表示x前面不同数的个数
        x += x & -x;
    }
}
int getsum(int x) {
    int res = 0;
    while(x > 0) {
        res += c[x];
        x -= x & -x;
    }
    return res;
}
int main(int argc, const char * argv[]) {
    int n, q;
    while(scanf("%d %d", &n, &q) != EOF) {
        memset(c, 0, sizeof(c));
        memset(pre, 0, sizeof(pre));
        memset(mp, 0, sizeof(mp));
        for (int i = 1; i <= n; i ++) {
            scanf("%d", &a[i]);
            a[i + n] = a[i];//拼接数组
        }
        for (int i = 0; i < q; i ++) {
            int l, r;
            scanf("%d %d", &l, &r);
            p[i].l = r;
            p[i].r = l + n;
            p[i].id = i;//记录点
        }
        sort(p, p + q);
        n <<= 1;
        for (int i = 1; i <= n; i ++) {
            pre[i] = mp[a[i]];
            mp[a[i]] = i;
        }//mp记录a[i]最后一次出现的位置,pre记录的是这个a[i]在i位置之前有无出现过,没出现过就为0,出现过就为上一次出现的位置
        int j = 1;
        for (int i = 0; i < q; i ++) {
            for (; j <= p[i].r; j ++) {
                if (pre[j])
                    updata(pre[j], -1);//如果j这个位置的a[j]出现过,那么pre[j]后面的减1
                updata(j, 1);//在j后面的加1
                //这样保证了如果1-r出现了相同的数字那么getsum(r)所代表的不同数字的值不变
            }//从上一个a[i]到这一个a[i]之间没有a[i]这个数字
            ans[p[i].id] = getsum(p[i].r) - getsum(p[i].l - 1);
        }
        for (int i = 0; i < q; i ++)
            printf("%d\n", ans[i]);
    }
    return 0;
}

对于 后面的updata操作可以画个图理解

(csdn我传不了图,就自己画把)

比如

数组a:。。。2.。。2.。。。2.。。5

a[3] = 2, a[6] = 2, a[10] = 2;

当j==3是,pre[3] = 0,正常操作,当j==6时pre[6]=3,那么3以后的减一,6以后的加1,这样就造成了6以后的不变,而3-6则减1

扫描二维码关注公众号,回复: 2345416 查看本文章

。。。2.。。2.。。。2.。。5

                          -1         不变

可以这样理解,getsum(6)2的个数仍是1个

而  getsum(4)则不包含2,因为 r是递增的,所以不会用到getsum(4),用到的时候也是区间的 L 在4这里但是getsum(4)不包含2,所以getsum(6)- getsum(4)表示的就只有1个2即[4,6]区间中2的个数只有1个

往后推也是一个道理

好难啊!!!!

猜你喜欢

转载自blog.csdn.net/henu_jizhideqingwa/article/details/81173970
今日推荐