BZOJ 3173: [Tjoi2013]最长上升子序列

3173: [Tjoi2013]最长上升子序列

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 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

Sample Output

1
1
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

posted @ 2016-11-21 23:08  YouSiki  阅读(461)  评论(0编辑  收藏  举报