牛客网暑期ACM多校训练营(第一场)J——Different Integers 区间不同数
程序员文章站
2024-03-15 20:11:48
...
题意:
给你一个n个数的数列和q次询问l, r,需要回答a[1]...a[l], a[r]...a[n]的不同数的个数。
n,q <=1e5,a[i] <= n
题解:
用莫队的写法比较简单,这里不再多说。
说一下树状数组的写法。普通的求区间不同数可以用离线树状数组,方法是按查询的r从小到大排序, 然后遍历整个数组,用树状数组记录这个值目前为止最后出现的位置,在这个位置加一, 并且边更新询问的指针。查询只用sum(r) - sum(l-1)即可。
这个题有点不同。但是我们可以求出a[1]...a[l], a[r]...a[n]里面没有出现的数的个数,即求出a[l+1]....a[r]里面出现且不在其他位置出现的值的个数。
现在我们的查询相当于询问a[l]....a[r]里面出现的且不在外面出现的不同数的个数。
我们额外增加一个数组first保存每个数第一次出现的位置。
方法还是按照查询的r从小到大排序,遍历整个数组,如果当前位置i是a[i]的最后位置,那么我们只需要在他第一次出现的位置处用树状数组+1.这样查询区间和就是在这个区间内出现且不在其他位置出现的不同数的个数了。
代码:
#include<bits/stdc++.h>
#define fir first
#define sec second
#define pii pair<int,int>
#define debug(x) cout<<#x<<" = "<<x<<endl;
using namespace std;
typedef long long ll;
const int MOD = 1e9 + 7;
const int MAXN = 1e5 + 5;
int fir[MAXN], last[MAXN];
int tree[MAXN];
int n, q;
struct Query {
int l, r;
bool operator < (const Query & x) const {
return r < x.r;
}
int id;
} que[MAXN];
void add(int i, int v) {
while(i <= n) {
tree[i] += v;
i += i&-i;
}
}
int query(int i) {
int ret = 0;
while(i) {
ret += tree[i];
i -= i&-i;
}
return ret;
}
int ans[MAXN];
int a[MAXN];
int main() {
#ifdef LOCAL
freopen("input.txt", "r", stdin);
#endif
while(~scanf("%d %d", &n, &q)) {
memset(fir, 0, sizeof(fir));
memset(tree, 0, sizeof(tree));
int tot = 0;
for(int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
if(!fir[a[i]]) fir[a[i]] = i, tot++;
last[a[i]] = i;
}
for(int i = 1; i <= q; i++) scanf("%d %d", &que[i].l, &que[i].r), que[i].id = i;
sort(que + 1, que + 1 + q);
int p = 1;
for(int i = 1; i <= n; i++) {
while(p <= q && que[p].r == i) {
int id = que[p].id;
ans[id] = tot - query(i) + query(que[p].l);
p++;
}
if(last[a[i]] == i) add(fir[a[i]], 1);
}
for(int i = 1; i <= q; i++) printf("%d\n", ans[i]);
}
}