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
3
0 0 2
0 0 2
Sample Output
1
1
2
1
2
HINT
100%的数据 n<=100000
Source
分析
本来是想找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 }
后记:
大概是刚考完NOIP,又要准备学考,大家的刷题兴致不高啊,居然被我轻松拿了Day榜,(*^_^*)/。
No. | User | Nick Name | AC | Submit | Ratio |
1 | YOUSIKI | ねえ、あなたは知っていますか、桜の行方の速度は秒速5センチメートル | 5 | 6 | 83.333% |
@Author: YouSiki