BZOJ5484(LIS性质+树状数组)
学习的这篇题解。
结论:
1.直观感受一下会发现找到LIS,LIS里的东西相对位置是不会变的,其他的移一移总会排序成功的,所以其他的就是最小集合了,第一问的答案就是n-LIS;
2.寻找字典序第k小的集合,相当于是寻找字典序第k大的LIS,然后把这个LIS删去,就是第二问的答案集合。
前置技能:
树状数组,及树状数组求LIS。
解决方法(请先看代码):
1.树状数组bit[i]求LIS的同时再维护一下“以比i大的数字为开头、这个LIS长度下的序列的数量”。数量超过maxk的时候min一下砍掉(是一种常见手法),因为多了也没有用它只会询问maxk以内的,否则有可能爆longlong。
2.用vector存下每个长度的LIS是以哪些位置为起点,然后按长度从大到小枚举,看看第k个是哪个LIS,标记这些数字。因为之前维护了数量,所以这时就不用从1开始一个一个枚举到k了,一下砍下去一段。
1 #pragma comment(linker, "/STACK:1024000000,1024000000") 2 #include <cstdio> 3 #include <cstring> 4 #include <cstdlib> 5 #include <cmath> 6 #include <ctime> 7 #include <cctype> 8 #include <climits> 9 #include <iostream> 10 #include <iomanip> 11 #include <algorithm> 12 #include <string> 13 #include <sstream> 14 #include <stack> 15 #include <queue> 16 #include <set> 17 #include <map> 18 #include <vector> 19 #include <list> 20 #include <fstream> 21 #include <bitset> 22 #define init(a, b) memset(a, b, sizeof(a)) 23 #define rep(i, a, b) for (int i = a; i <= b; i++) 24 #define irep(i, a, b) for (int i = a; i >= b; i--) 25 using namespace std; 26 27 typedef double db; 28 typedef long long ll; 29 typedef unsigned long long ull; 30 typedef pair<int, int> P; 31 const int inf = 0x3f3f3f3f; 32 const ll INF = 1e18; 33 34 template <typename T> void read(T &x) { 35 x = 0; 36 int s = 1, c = getchar(); 37 for (; !isdigit(c); c = getchar()) 38 if (c == '-') s = -1; 39 for (; isdigit(c); c = getchar()) 40 x = x * 10 + c - 48; 41 x *= s; 42 } 43 44 template <typename T> void write(T x) { 45 if (x < 0) x = -x, putchar('-'); 46 if (x > 9) write(x / 10); 47 putchar(x % 10 + '0'); 48 } 49 50 template <typename T> void writeln(T x) { 51 write(x); 52 puts(""); 53 } 54 55 const int maxn = 1e5 + 5; 56 const ll maxk = 1e18; 57 58 int n, a[maxn]; 59 ll k; 60 struct Arr { 61 int len; 62 ll cnt; 63 Arr() { 64 len = 0, cnt = 0; 65 } 66 Arr(int a, ll b) { 67 len = a, cnt = b; 68 } 69 }dp[maxn], bit[maxn]; 70 vector<int> v[maxn]; 71 bool mark[maxn]; 72 73 void Modify(Arr &a, Arr b) { 74 if (a.len > b.len) return; 75 if (a.len < b.len) { 76 a = b; 77 return; 78 } 79 a.cnt = min(maxk, a.cnt + b.cnt); 80 } 81 82 void Update(int x, Arr val) { 83 for (; x; x -= x&-x) 84 Modify(bit[x], val); 85 } 86 87 Arr Query(int x) { 88 Arr ret(0, 1); 89 for (; x <= n; x += x&-x) 90 Modify(ret, bit[x]); 91 return ret; 92 } 93 94 int main() { 95 read(n), read(k); 96 rep(i, 1, n) read(a[i]); 97 //树状数组维护一个后缀最大序列(的长度和此长度下的LIS个数),规则是:1.长度最大;2.如果长度相同,累加数量 98 irep(i, n, 1) {//比较字典序是从左往右,所以将此长度下的cnt集中在左端,故而倒序 99 dp[i] = Query(a[i] + 1); 100 v[++dp[i].len].push_back(i); 101 Update(a[i], dp[i]); 102 } 103 104 int LIS = Query(1).len; 105 for (int i = LIS, pos = 1; i; --i) { 106 for (int j = v[i].size() - 1; ~j; --j) { 107 //找字典序大的,所以倒着找。vector的插入导致其中的a[p]必然升序 108 int p = v[i][j]; 109 if (dp[p].cnt < k) {//以当前数字为开头的所有LIS的数量都无法满足k的需求,则这个开头被pass 110 k -= dp[p].cnt; 111 } else {//说明要找的LIS以当前这个数字为开头 112 mark[a[p]] = true; 113 while (pos < p) dp[pos++].cnt = 0; 114 //选定这个数以后,因为是找LIS,所以它之前的数不能再选中 115 break; 116 } 117 } 118 } 119 120 writeln(n - LIS); 121 rep(i, 1, n) { 122 if (!mark[i]) 123 writeln(i); 124 } 125 return 0; 126 }