1. 程式人生 > >【bzoj3173】最長上升子序列

【bzoj3173】最長上升子序列

節點 return hup 位置 一個 ostream online %d pre

Portal --> bzoj3173

Solution

  感覺自己需要智力康復qwq

  首先題目給的這個序列肯定是一個\(1-n\)的排列,並且插入的順序是從小到大

  仔細思考一下會發現如果知道了最終的序列,問題就比較好解決了,這裏提供一種用線段樹的做法:

  如果知道了最終的序列,記數字\(i\)在該序列中的位置為\(loc[i]\),那麽我們按照\(i\)從小到大的順序,查詢結尾在\([1,loc[i])\)的這段位置中的最長上升子序列的最大值\(mx\),並將\(mx+1\)作為以\(loc[i]\)位置為結尾的答案,插入到線段樹中\(loc[i]\)對應的節點裏,復雜度是\(O(nlogn)\)

  然後現在的問題是怎麽求最終的序列

  這個可以用平衡樹來寫,不過其實也可以用線段樹來寫

  考慮反過來確定每一個數在最終序列中的位置,因為是反過來考慮的,所以一開始的時候每一個位置都有一個數,然後我們根據讀入的插入位置,按照\(n-1\)的順序,找到當前這個數的位置,然後將它刪掉(也就是對應的線段樹節點的\(sum-1\)

?  具體一點就是比如當前考慮到第\(i\)個數,讀入這個數應該要插入在\(a[i]\)的位置後面,也就是應該在當前這個序列的第\(a[i]+1\)個位置,那麽找到這個位置,然後把這個位置刪掉,這樣就可以得到還沒有插入這個數之前的序列的位置集合了,這部分的復雜度也是\(O(nlogn)\)

?  然後就十分愉快地做完啦

  

  代碼大概長這個樣子

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int MAXN=100010,SEG=MAXN*4;
namespace Seg{/*{{{*/
    int ch[SEG][2],sum[SEG],mx[SEG];
    int n,tot;
    void pushup(int x){
        sum[x]=sum[ch[x][0]]+sum[ch[x][1]];
        mx[x]=max(mx[ch[x][0
]],mx[ch[x][1]]); } void _build(int x,int l,int r){ sum[x]=0; mx[x]=0; if (l==r){sum[x]=0; return;} int mid=l+r>>1; ch[x][0]=++tot; _build(ch[x][0],l,mid); ch[x][1]=++tot; _build(ch[x][1],mid+1,r); pushup(x); } void build(int _n){n=_n; tot=1; _build(1,1,n);} void _update(int x,int d,int lx,int rx,int delta){ if (lx==rx) {sum[x]+=delta;mx[x]+=delta;return;} int mid=lx+rx>>1; if (d<=mid) _update(ch[x][0],d,lx,mid,delta); else _update(ch[x][1],d,mid+1,rx,delta); pushup(x); } void update(int d,int delta){_update(1,d,1,n,delta);} int _query_mx(int x,int l,int r,int lx,int rx){ if (l<=lx&&rx<=r) return mx[x]; int mid=lx+rx>>1,ret=0; if (l<=mid) ret=max(ret,_query_mx(ch[x][0],l,r,lx,mid)); if (r>mid) ret=max(ret,_query_mx(ch[x][1],l,r,mid+1,rx)); return ret; } int query(int l,int r){return _query_mx(1,l,r,1,n);} int _get_loc(int x,int lx,int rx,int k){ if (lx==rx) return lx; int mid=lx+rx>>1; if (sum[ch[x][0]]>=k) return _get_loc(ch[x][0],lx,mid,k); else return _get_loc(ch[x][1],mid+1,rx,k-sum[ch[x][0]]); } int get_loc(int k){return _get_loc(1,1,n,k);} };/*}}}*/ int loc[MAXN],a[MAXN],b[MAXN]; int n,m,ans; int main(){ #ifndef ONLINE_JUDGE freopen("a.in","r",stdin); #endif scanf("%d",&n); for (int i=1;i<=n;++i) scanf("%d",b+i),++b[i]; Seg::build(n); for (int i=1;i<=n;++i) Seg::update(i,1); for (int i=n;i>=1;--i){ loc[i]=Seg::get_loc(b[i]); Seg::update(loc[i],-1); } for (int i=1;i<=n;++i) a[loc[i]]=i; Seg::build(n); ans=0; int tmp; for (int i=1;i<=n;++i){ if (loc[i]>1) tmp=Seg::query(1,loc[i]-1); else tmp=0; Seg::update(loc[i],tmp+1); ans=max(ans,tmp+1); printf("%d\n",ans); } }

【bzoj3173】最長上升子序列