码迷,mamicode.com
首页 > 其他好文 > 详细

One Occurrence 线段树离线维护最小值

时间:2020-01-03 21:00:45      阅读:80      评论:0      收藏:0      [点我收藏+]

标签:nlogn   key值   strong   下标   单点更新   using   include   维护   print   

  题意:一个长度为n的序列(记为A[i]),q次查询,每次输出查询区间内任意一个只出现一次的数字,没有则输出0。

  

  思路:线段树结点存元素的位置和上一个相同元素出现过的位置(没有则为0,记为pos),线段树维护区间结点最小值,结点封装在pair里,第一key值为前一个相同元素出现的位置,先将查询存下来,对于1到n的每一个前缀【1,r】,维护元素的最右边出现的pos值,例如1 1 2 3 2这个区间,他们的pos值分别是inf,1,inf,0,3。对于已经知道了【1,r】,要得到【1,r+1】,若A【r+1】在前面出现过,则将前面元素的pos值改为inf,再记录自己的pos值。

像1 1 2 3 2 加进来一个3 ,则pos值的变化为inf,1,inf,0,3—> inf,1,inf,inf,4。这个用线段树的单点更新维护,再将询问以右端点排序,从1开始枚举序列的右端点,若此时的右端点和询问的右端点一致,区间查询一次询问的区间,若得到的pos最小值比l小,代表这个数的上一个值出现的位置再区间外边,则这个数就是查询的答案了,若最小值不符合,则代表没有符合题意的数字。

 

AC代码:复杂度nlogn

 

 1 #include<iostream>
 2 #include<algorithm>
 3 #include<stdio.h>
 4 using namespace std;
 5 const int maxn=5e5+10,inf=0x3f3f3f3f;
 6 typedef pair<int,int> pa;
 7 int n,q;
 8 int A[maxn],ans[maxn],pos[maxn];
 9 pa a[maxn<<2];
10 
11 struct Q{
12     int l,r,id;
13 }p[maxn];
14 
15 void update(int pos,int val,int l,int r,int rt){
16     if(l==r){
17         a[rt].first=val;
18         a[rt].second=pos;//注意这里的pos值代表元素的下标
19         return;
20     }
21     int m=l+r>>1;
22     if(pos<=m)
23         update(pos,val,l,m,rt<<1);
24     else
25         update(pos,val,m+1,r,rt<<1|1);
26     a[rt]=min(a[rt<<1],a[rt<<1|1]);
27 }
28 
29 pa query(int L,int R,int l,int r,int rt){
30     if(L<=l&&r<=R){
31         return a[rt];
32     }
33     int m=l+r>>1;
34     pa z;z.first=inf;
35     if(L<=m)
36         z=min(z,query(L,R,l,m,rt<<1));
37     if(R>m)
38         z=min(z,query(L,R,m+1,r,rt<<1|1));
39     return z;
40 }
41 
42 bool cmp(Q a,Q b){
43     return a.r==b.r ? a.l<b.l : a.r < b.r;
44 }
45 
46 int main(){
47     scanf("%d",&n);
48     for(int i=1;i<=n;i++){
49         scanf("%d",&A[i]);
50     }
51     cin>>q;
52     for(int i=1;i<=q;i++){
53         scanf("%d%d",&p[i].l,&p[i].r);
54         p[i].id=i;
55     }
56     sort(p+1,p+1+q,cmp);
57     for(int i=1,j=1;i<=n;i++){
58         if(pos[A[i]]) update(pos[A[i]],inf,1,n,1);//如果之前出现过,将之前出现数字的pos值改为inf
59         update(i,pos[A[i]],1,n,1);//更新现在的右端点值
60         for(;p[j].r==i;j++){
61             pa now=query(p[j].l,p[j].r,1,n,1);
62             if(now.first<p[j].l){
63                 ans[p[j].id]=A[now.second];
64             }
65         }
66         pos[A[i]]=i;//维护pos数组,pos【i】代表i数字上次出现的位置
67     }
68     for(int i=1;i<=q;i++){
69         printf("%d\n",ans[i]);
70     }
71     return 0;
72 }
73 /*
74 6
75 1 1 2 2 3 3
76 1
77 1 3
78 */

One Occurrence 线段树离线维护最小值

标签:nlogn   key值   strong   下标   单点更新   using   include   维护   print   

原文地址:https://www.cnblogs.com/qq2210446939/p/12146586.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!