链接: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个
往后推也是一个道理
好难啊!!!!