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 }

 

posted @ 2019-03-19 10:44  AlphaWA  阅读(239)  评论(0编辑  收藏  举报