1. 程式人生 > 實用技巧 >Wi Know (思維+線段樹)

Wi Know (思維+線段樹)

Wi Know

題意:在字串中找ABAB形的子序列,輸出字典序最小的AB

題解:列舉每個位置作為B1,在該位置與這個字元的下一個位置B2之間查詢最小的A2,而在B1之前,所有字元都可以作為A1,已經把它們的下一個A2放到了線段樹裡。

AC_Code:

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 typedef long long ll;
 4 #define endl '\n'
 5 const int mod=6000;
 6 const int maxn=5e5+10;
 7 const int inf=0x3f3f3f3f
; 8 9 int a[maxn],nxt[maxn],pos[maxn],n; 10 int tree[maxn<<2],lz[maxn<<2]; 11 pair<int,int>ans=make_pair(inf,inf); 12 13 int query(int rt,int l,int r,int ql,int qr){ 14 if( ql<=l && r<=qr ) return tree[rt]; 15 int mid=(l+r)>>1; 16 int minn=inf; 17
if( ql<=mid ) minn=min(minn,query(rt<<1,l,mid,ql,qr)); 18 if( qr>mid ) minn=min(minn,query(rt<<1|1,mid+1,r,ql,qr)); 19 return minn; 20 } 21 22 void update(int rt,int l,int r,int pos){//線段樹更新區間字典序最小的字元 23 if( l==r ) tree[rt]=a[pos]; 24 else{ 25 int mid=(l+r)>>1
; 26 if( pos<=mid ) update(rt<<1,l,mid,pos); 27 else update(rt<<1|1,mid+1,r,pos); 28 tree[rt]=min(tree[rt<<1],tree[rt<<1|1]); 29 } 30 } 31 32 int main() 33 { 34 memset(tree,inf,sizeof(tree)); 35 scanf("%d",&n); 36 for(int i=1;i<=n;i++) scanf("%d",&a[i]); 37 for(int i=n;i>=1;i--){ 38 nxt[i]=pos[a[i]]; //nxt陣列存a[i]下一次出現的位置 39 pos[a[i]]=i; 40 } 41 for(int i=1;i<=n;i++){ 42 if( nxt[i]==0 ) continue; 43 ans=min(ans, make_pair(query(1,1,n,i+1,nxt[i]-1),a[i])); 44 update(1,1,n,nxt[i]); 45 } 46 if( ans.first!=inf && ans.second!=inf ) printf("%d %d\n",ans.first, ans.second); 47 else printf("-1\n"); 48 return 0; 49 }