Wi Know——思维(线段树辅助)

传送:

https://cn.vjudge.net/problem/Kattis-wiknow

题意:

在字符串中找形如ABAB的子序列,要求AB字典序最小。

思路:

这道题最重要的性质:如果存在形如ABAB的子序列,那么一定可以找到一个A1B1A2B2,满足A1A2之间没有其它A,B1B2之间没有其它B。

先预处理每个字符右边一个的位置。扫描一遍字符串,把每个字符右边一个加入线段树,同时枚举B1,在B1,B2之间区间最小查询,这个最小值一定在B1左边出现过,因为更新线段树时是加入每个字符右边的。

code:

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 typedef pair<int,int> P;
 4 const int N=400000+10;
 5 const int INF=0x3f3f3f3f;
 6 int dat[4*N];
 7 int a[N],nex[N],r[N],n;
 8 void update(int pos,int k,int l,int r)
 9 {
10     if(r-l==1){dat[k]=a[pos];return;}
11     int m=(l+r)>>1;
12     if(pos<m)update(pos,k<<1,l,m);
13     else update(pos,(k<<1)|1,m,r);
14     dat[k]=min(dat[k<<1],dat[(k<<1)|1]);
15 }
16 int query(int a,int b,int k,int l,int r)
17 {
18     if(l==r)return INF;
19     if(a<=l&&b>=r)return dat[k];
20     int m=(l+r)>>1,mi=INF;
21     if(a<m)mi=min(mi,query(a,b,k<<1,l,m));
22     if(b>m)mi=min(mi,query(a,b,(k<<1)|1,m,r));
23     return mi;
24 }
25 int main()
26 {
27     memset(dat,0x3f,sizeof(dat));
28     scanf("%d",&n);
29     for(int i=1;i<=n;++i)
30         scanf("%d",a+i);
31     for(int i=n;i>=1;--i)//处理每个字符右边一个的位置
32     {
33         nex[i]=r[a[i]];
34         r[a[i]]=i;
35     }
36     P ans(INF,INF);
37     for(int i=1;i<=n;++i)
38     {
39         if(!nex[i])continue;
40         int v=query(i+1,nex[i],1,1,n+1);//在当前位置与下一个位置之间查询最小值
41         ans=min(ans,make_pair(v,a[i]));
42         update(nex[i],1,1,n+1);//把当前字符下一个更新进线段树
43     }
44     if(ans.first==INF||ans.second==INF)
45         puts("-1");
46     else printf("%d %d\n",ans.first,ans.second);
47     return 0;
48 }
View Code

 

猜你喜欢

转载自www.cnblogs.com/judp/p/11330758.html
今日推荐