BZOJ 3173: [Tjoi2013]最长上升子序列
生活随笔
收集整理的這篇文章主要介紹了
BZOJ 3173: [Tjoi2013]最长上升子序列
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
3173: [Tjoi2013]最長上升子序列
Time Limit:?10 Sec??Memory Limit:?128 MBSubmit:?1524??Solved:?797
[Submit][Status][Discuss]
Description
給定一個序列,初始為空。現在我們將1到N的數字插入到序列中,每次將一個數字插入到一個特定的位置。每插入一個數字,我們都想知道此時最長上升子序列長度是多少?Input
第一行一個整數N,表示我們要將1到N插入序列中,接下是N個數字,第k個數字Xk,表示我們將k插入到位置Xk(0<=Xk<=k-1,1<=k<=N)Output
N行,第i行表示i插入Xi位置后序列的最長上升子序列的長度是多少。Sample Input
30 0 2
Sample Output
11
2
HINT
?
100%的數據 n<=100000
?
Source
[Submit][Status][Discuss]?
分析
本來是想找Treap的練手題,考完NOIP放松一下心情的,結果看完題發現根本不用Treap……
?
首先,因為加入的元素是越來越大的,所以每次加入之后對于后面的元素的LIS的DP值(即以其結尾的最長上升子序列長度)不會改變,而前面的更不會改變。也就是說,最終序列的DP數組就是逐步加入的DP值了。所以只需要想方設法求出最終序列,再做一遍O(NlogN)的LIS問題即可。
求解最終的序列方法多種多樣,可以用Treap暴力維護插入操作,也可以逆著推出最終的序列,我選擇了后者。
對于第N個插入的元素,其插入的位置就是最終的位置。當它找到了最終位置之后,就把那個位置改為空。類似的,每個元素的最終位置,就是此時的第Xk個非空位置。這個操作用線段樹維護即可輕松做到O(logN),當然也可以樹狀數組+二分做到O(log^2N),常數小也許跑得反而更快,(lll¬ω¬)。
?
代碼
1 #include <bits/stdc++.h> 2 3 using namespace std; 4 5 #define low lower_bound 6 #define upp upper_bound 7 8 const int N = 100005; 9 const int inf = 0x3f3f3f3f; 10 11 int n; 12 int pos[N]; 13 int num[N]; 14 int ans[N]; 15 int stk[N]; 16 17 struct node 18 { 19 int lt, rt, sum; 20 21 node (void) : 22 lt (0), rt (0), sum (0) {}; 23 }; 24 25 node tree[N << 2]; 26 27 void build (int p, int l, int r) 28 { 29 node &t = tree[p]; 30 31 t.lt = l; 32 t.rt = r; 33 34 t.sum = r - l + 1; 35 36 if (l != r) 37 { 38 int mid = (l + r) >> 1; 39 40 build (p << 1, l, mid); 41 build (p << 1 | 1, mid + 1, r); 42 } 43 } 44 45 void change (int p, int pos, int val) 46 { 47 node &t = tree[p]; 48 49 if (t.lt != t.rt) 50 { 51 int mid = (t.lt + t.rt) >> 1; 52 53 if (pos <= mid) 54 change (p << 1, pos, val); 55 else 56 change (p << 1 | 1, pos, val); 57 58 t.sum = tree[p << 1].sum + tree[p << 1 | 1].sum; 59 } 60 else 61 t.sum = val; 62 } 63 64 int query (int p, int val) 65 { 66 node &t = tree[p]; 67 68 if (t.lt != t.rt) 69 { 70 int tmp = tree[p << 1].sum; 71 72 if (val <= tmp) 73 return query (p << 1, val); 74 else 75 return query (p << 1 | 1, val - tmp); 76 } 77 else 78 return t.lt; 79 } 80 81 signed main (void) 82 { 83 scanf ("%d", &n); 84 85 for (int i = 1; i <= n; ++i) 86 scanf ("%d", pos + i); 87 88 build (1, 1, n); 89 90 for (int i = n; i >= 1; --i) 91 { 92 int t = query (1, pos[i] + 1); 93 num[t] = i, change (1, t, 0); 94 } 95 96 memset (stk, inf, sizeof(stk)); 97 98 for (int i = 1; i <= n; ++i) 99 { 100 *low (stk, stk + i, num[i]) = num[i]; 101 ans[num[i]] = low (stk, stk + i, num[i]) - stk; 102 } 103 104 for (int i = 1; i <= n; ++i) 105 ans[i] = max (ans[i], ans[i - 1]); 106 107 for (int i = 1; i <= n; ++i) 108 printf ("%d\n", ans[i] + 1); 109 } BZOJ_3173.cpp?
后記:
大概是剛考完NOIP,又要準備學考,大家的刷題興致不高啊,居然被我輕松拿了Day榜,(*^_^*)/。
?
| No. | User | Nick Name | AC | Submit | Ratio |
| 1 | YOUSIKI | ねえ、あなたは知っていますか、桜の行方の速度は秒速5センチメートル | 5 | 6 | 83.333% |
?
@Author: YouSiki
轉載于:https://www.cnblogs.com/yousiki/p/6087448.html
總結
以上是生活随笔為你收集整理的BZOJ 3173: [Tjoi2013]最长上升子序列的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Spring配置汇总
- 下一篇: “增查改删”的语句