雙端佇列xLIS問題
阿新 • • 發佈:2020-10-22
題目大意
有 \(N\) 個數 \(A_i\) ,他準備將他們依次插入一個雙端佇列(每次可以在頭或尾插入一個元素),最後將
整個佇列從尾到頭看成一個序列,求出最長上升子序列的長度 。他想知道 , \(L\) 的最大值是多少。
分析
很簡單,考慮一個數,構造有它的最長上升子序列
把比他小的放他前面,比他大的放它後面
比他小的最優的就是以某一個比他小的數為開頭的最長下降子序列的長度
比他大的最優的就是以它為開頭的最長上升子序列的長度
最後相加取最大值即可
線段樹維護 \(O(N \log N)\)
\(Code\)
#include<cstdio> #include<iostream> #include<algorithm> #define ls (k << 1) #define rs (ls | 1) using namespace std; const int N = 1e5 + 5; int n , a[N] , f[N] , g[N] , V , seg[N * 4] , b[N]; void build(int l , int r , int k) { if (l == r) { seg[k] = 0; return; } int mid = (l + r) >> 1; build(l , mid , ls) , build(mid + 1 , r , rs); seg[k] = max(seg[ls] , seg[rs]); } void update(int l , int r , int k , int x , int v) { if (l == r && l == x) { seg[k] = v; return; } int mid = (l + r) >> 1; if (x <= mid) update(l , mid , ls , x , v); else update(mid + 1 , r , rs , x , v); seg[k] = max(seg[ls] , seg[rs]); } int query(int l , int r , int k , int tl , int tr) { if (tl > tr) return 0; if (tl <= l && r <= tr) return seg[k]; int mid = (l + r) >> 1 , res = 0; if (tl <= mid) res = max(res , query(l , mid , ls , tl , tr)); if (tr > mid) res = max(res , query(mid + 1 , r , rs , tl , tr)); return res; } int main() { freopen("dequexlis.in" , "r" , stdin); freopen("dequexlis.out" , "w" , stdout); scanf("%d" , &n); for(register int i = 1; i <= n; i++) scanf("%d" , a + i) , b[i] = a[i]; sort(b + 1 , b + n + 1); int len = unique(b + 1 , b + n + 1) - b - 1; for(register int i = 1; i <= n; i++) a[i] = lower_bound(b + 1 , b + len + 1 , a[i]) - b; V = n + 1; build(1 , n , 1); for(register int i = n; i; i--) { f[i] = query(1 , n , 1 , a[i] + 1 , V) + 1; update(1 , n , 1 , a[i] , f[i]); } build(1 , n , 1); for(register int i = n; i; i--) { g[i] = query(1 , n , 1 , 1 , a[i] - 1) + 1; update(1 , n , 1 , a[i] , g[i]); } build(1 , n , 1); for(register int i = 1; i <= n; i++) update(1 , n , 1 , a[i] , g[i]); int ans = 0; for(register int i = 1; i <= n; i++) ans = max(ans , query(1 , n , 1 , 1 , a[i] - 1) + f[i]); printf("%d" , ans); }