- https://ac.nowcoder.com/acm/contest/139/J
- 树状数组学习:https://www.cnblogs.com/acgoto/p/8583952.html
- 题意:给你一个数组, q次询问, 每次询问都会有1个[l, r] 求 区间[1,l] 和 [r, n] 中 数字的种类是多少。
- 思路:first数组记录每个值第一次出现的位置,last数组记录每个值最后出现的位置,然后按照q次询问的
- r由小到大排序,tot记录这个序列的不同数字的个数,从1开始一直遍历到最后一个q[m].r在这个过程中,
- 会经历许多个点,当这个点位置为R并且他里面的值a[R]最后一次出现的位置也是R那么就得在他第一次出现的位置
- 标记一下,也就是树状数组里面进行维护,给first[a[R]]++,同时,tot--,因为访问到了一个数的终止位置,这个终止位置
- 可能在1-q[i].L里面,也可能在q[i].L-q[i].R 里面,不管在哪,碰到终止位置我们tot先减去1,然后树状数组求和1-q[i].L被标记
- 上的数,(被标记的含义是这个数终止位置已经出现,并且tot已经减去了它,所以我需要标记,来判断他到底是在
- 1-q[i].L里面,还是在q[i].L-q[i].R 里面,如果在1-q[i].L里面,那么你求和肯定能够求出它来,这一个q[i]询问的结果,
- 自然就是tot-询问的sum值)。
-
#include<bits/stdc++.h> using namespace std; const int maxn =2e5+100; int a[maxn],ans[maxn],n; int last[maxn],cnt[maxn]; int m,tot,first[maxn],r; struct node { int l,r,id; bool operator<(const node&b)const { return r<b.r; } } q[maxn]; void add(int x) { while(x<=n) { cnt[x]++; x+=x&(-x); } } int query(int x) { int ret=0; while(x) { ret+=cnt[x]; x-=x&(-x); } return ret; } int main() { while(~scanf("%d%d",&n,&m)) { memset(cnt,0,sizeof(cnt)); memset(last,0,sizeof(last)); memset(first,0,sizeof(first)); tot=0; for(int i=1; i<=n; i++) { scanf("%d",&a[i]); if(first[a[i]]==0) { first[a[i]]=i; tot++; } last[a[i]]=i; } for(int i=1; i<=m; i++) { scanf("%d%d",&q[i].l,&q[i].r); q[i].id=i; } sort(q+1,q+1+m); r=1; for(int i=1; i<=m; i++) { while(r<q[i].r) { if(last[a[r]]==r) { add(first[a[r]]); tot--; } r++; } ans[q[i].id]=tot+query(q[i].l); } for(int i=1; i<=m; i++) printf("%d\n",ans[i]); } return 0; }
J-Different Integers-树状数组-区间不同数查询
最新推荐文章于 2021-01-24 10:00:36 发布