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 }