解:考场上爆0了......
回想怎么求两个排列的最长公共子序列。
回想怎么求1~n每个数恰出现两次的两个序列的最长公共子序列。就是每个数替换为它在另一个序列里的出现位置,降序。
所以我们可以把这每个空位都倒序填入m个数,然后暴力,最后输出方案。
考虑优化。发现在每个空位的时候,这m个数都是单降的。直接拿指针在单调栈上扫,可以O(top + m)转移。
关于记录方案,每个位置记录以它结尾的lis中的前一个非-1位置,以及在这之间有多少个-1。
然后就可以把空间优化到O(n)。
1 #include <bits/stdc++.h> 2 3 typedef long long LL; 4 const int N = 200010; 5 const LL INF = 0x3f3f3f3f3f3f3f3fll;; 6 7 struct Node { 8 int a, b; 9 Node(int A = 0, int B = 0) { 10 a = A; 11 b = B; 12 } 13 }frp[N], fr[N]; 14 15 LL a[N], b[N], p[N]; 16 int top, n, m; 17 bool vis[N]; 18 19 int main() { 20 scanf("%d", &n); 21 for(int i = 1; i <= n; i++) { 22 scanf("%lld", &a[i]); 23 } 24 scanf("%d", &m); 25 for(int i = 1; i <= m; i++) { 26 scanf("%lld", &b[i]); 27 } 28 std::sort(b + 1, b + m + 1); 29 std::reverse(b + 1, b + m + 1); 30 a[n + 1] = INF; 31 for(int i = 1; i <= n + 1; i++) { 32 if(a[i] != -1) { 33 int l = 0, r = top; 34 while(l < r) { 35 int mid = (l + r + 1) >> 1; 36 if(p[mid] < a[i]) l = mid; 37 else r = mid - 1; 38 } 39 fr[i] = frp[r]; 40 if(r == top) { 41 p[++top] = a[i]; 42 frp[top] = Node(i, 0); 43 } 44 else if(p[r + 1] > a[i]) { 45 p[r + 1] = a[i]; 46 frp[r + 1] = Node(i, 0); 47 } 48 } 49 else { 50 int p1 = top; 51 for(int j = 1; j <= m; j++) { 52 while(p1 && p[p1] >= b[j]) { 53 p1--; 54 } 55 Node temp = frp[p1]; 56 temp.b++; 57 if(p1 == top) { 58 p[++top] = b[j]; 59 frp[top] = temp; 60 } 61 else if(p[p1 + 1] > b[j]) { 62 p[p1 + 1] = b[j]; 63 frp[p1 + 1] = temp; 64 } 65 } 66 } 67 } 68 69 int now = frp[top].a, p1 = n, p2 = 1; 70 while(now) { 71 if(fr[now].b) { 72 while(p1 > now) p1--; 73 while(b[p2] >= a[now]) p2++; 74 for(int i = 1; i <= fr[now].b; i++) { 75 while(a[p1] != -1) p1--; 76 a[p1] = b[p2]; 77 vis[p2] = 1; 78 p2++; 79 } 80 } 81 now = fr[now].a; 82 } 83 84 p2 = 1; 85 for(int i = 1; i <= n; i++) { 86 if(a[i] == -1) { 87 while(vis[p2]) p2++; 88 a[i] = b[p2]; 89 p2++; 90 } 91 } 92 93 for(int i = 1; i <= n; i++) { 94 printf("%lld ", a[i]); 95 } 96 return 0; 97 }