【bzoj3173】最長上升子序列
阿新 • • 發佈:2018-06-16
節點 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】最長上升子序列