https://vjudge.net/contest/237022#problem/C
题意:有一组数1~n,按顺序给出每个数需要插入的位置(即第i数代表数字i要插入当前序列的第ai个),组合成新的序列,问每插入一个新的数,当前序列的最长上升子序列为多长?
解法:首先用线段树插空法将数列的最终位置还原出来,得到1~n的对应位置。而对于每次操作,当前序列的最长上升子序列长度等于1~n对应位置所构成的序列的最长上升子序列长度(可手算推导)。由于操作顺序从1~n,所以第i次操作只要对前i个数(即1~i)对应的位置组成的序列求LIS即可。
线段树插空法:从最后一个数开始,逆推每个数的对应位置,因为放置最后一个数时,其位置已经可以确定。用数组s记录每个操作数的插入位置,则逆推过程中,s[i]表示第i个前面需要留有s[i]个空格。线段树数中的a[i].l,a[i].r,a[i].n分别代表第i个结点的左右边界以及对应区间的空格数。记得初始化,即l==r时,该区间空格数为1。
LIS计算:用数组dp[]记录当前序列的最长上升子序列,len记录当前的最长长度,ans[i]记录数字i对应的位置。利用二分的方法,从1~n循环,每次寻找dp数组中第一个大于等于ans[i]的数,用k记录该数在dp数组中的位置(实质上k为ans[i]在满足序列上升的条件下放到dp数组中的对应位置),更新len(如果ans[i]比dp中的数都大,len=k;否则len保持不变),输出len,并用ans[i]代替dp[k](可手动模拟推导)。
1 //#include<bits/stdc++.h>
2 #include <iostream>
3 #include <algorithm>
4 #include <cstdio>
5 #include <cstring>
6 #include <string>
7 #include <cmath>
8 #include <cstdlib>
9 #include <queue>
10 #include <stack>
11 #include <map>
12 #include <vector>
13 #include <set>
14 #include <bitset>
15 #include <iomanip>
16 #define ms(a, b) memset(a, b, sizeof(a));
17 using namespace std;
18 typedef long long LL;
19 typedef pair<int, int> pii;
20 const int INF = 0x3f3f3f3f;
21 const int maxn = 1e5 + 10;
22 const int MAXN = 2e4 + 10;
23 const double eps = 1e-8;
24 const int mod = 1e9 + 7;
25 int n, len;
26 int s[maxn],ans[maxn], dp[maxn];
27
28 struct node {
29 int l, r, n;
30 }a[maxn<<2];
31
32 void build(int l, int r, int i) { //初始化各个结点的空格数
33 a[i].l = l;
34 a[i].r = r;
35 if(l == r) {
36 a[i].n = 1;
37 return;
38 }
39 int mid = (l + r) >> 1;
40 build(l, mid, i*2);
41 build(mid+1, r, i*2+1);
42 a[i].n = a[i*2].n + a[i*2+1].n;
43 return;
44 }
45
46 void update(int i, int x, int m) { //i为结点编号,数字m在当前序列的第x个空格
47 if(a[i].l == a[i].r) {
48 a[i].n = 0;
49 ans[m] = a[i].l;
50 return;
51 }
52 a[i].n--;
53 if(x <= a[2*i].n) update(2*i, x, m);
54 else update(2*i+1, x-a[2*i].n, m);
55 return;
56 }
57
58 int solve(int k) {
59 int l = 1, r = len;
60 while(l <= r) {
61 int mid = (l + r) >> 1;
62 if(dp[mid] > k) r = mid - 1;
63 else l = mid + 1;
64 }
65 return l; //l或者r+1都可以
66 }
67
68
69 int main() {
70 #ifdef local
71 freopen("case.in", "r", stdin);
72 // freopen("case.out", "w", stdout);
73 #endif
74 // ios::sync_with_stdio(false);
75 // cin.tie(0);
76 int T;
77 scanf("%d", &T);
78 int kase = 0;
79 while(T--) {
80 scanf("%d", &n);
81 build(1, n, 1);
82 for(int i = 1; i <= n; i++) {
83 scanf("%d", &s[i]);
84 dp[i] = 0;
85 }
86 for(int i = n; i > 0; i--) {
87 update(1, s[i]+1, i);
88 }
89 // for(int i = 1; i <= n; i++) cout << ans[i] << " ";
90 len = 1;
91 dp[1] = ans[1];
92 printf("Case #%d:\n", ++kase);
93 printf("%d\n", len);
94 for(int i = 2; i <= n; i++) {
95 int k = solve(ans[i]);
96 len = max(k, len);
97 dp[k] = ans[i];
98 printf("%d\n", len);
99 }
100 printf("\n");
101 }
102 // solve();
103 return 0;
104 }
来源:https://www.cnblogs.com/Sissi-hss/p/9410492.html