这题用也算是贪心吧,不过这里有一点数学思想。
对于要取掉K位的N位数,那么我们留下来的就是N-K位,我们不妨设 T = N - K; 那么我们选择的第一位数的后面就一定要有T-1个数让我们来选,因此第一个数的选择范围就是
[1, N-T+1],当我们选取了第一个数后(假设位置为P),我们第二个数的选择范围就是 [P+1, N-T-2]了,一次类推。由于我们要取的是最大的数,所以我们每次都选取区间内
的最大值,用线段树来作优化就可以了。
代码如下:
#include <cstdlib> #include <cstring> #include <cstdio> #include <algorithm> #include <queue> using namespace std; int N, K, leave; char s[500005]; struct Node { int l, r, pos, max; }e[2000005]; vector<int>v; void push_up(int p) { if (e[p<<1].max >= e[p<<1|1].max) { e[p].max = e[p<<1].max; e[p].pos = e[p<<1].pos; } else { e[p].max = e[p<<1|1].max; e[p].pos = e[p<<1|1].pos; } } void build(int p, int l, int r) { e[p].l = l, e[p].r = r; if (l != r) { int mid = (l + r) >> 1; build(p<<1, l, mid); build(p<<1|1, mid+1, r); push_up(p); } else { e[p].pos = l, e[p].max = s[l] - '0'; // 初始化 } } void query(int p, int l, int r, int &rec, int &pos) { if (l == e[p].l && r == e[p].r) { if (rec != e[p].max) { if (rec < e[p].max) { rec = e[p].max; pos = e[p].pos; } } else if (e[p].pos < pos){ pos = e[p].pos; } return; } int mid = (e[p].l + e[p].r) >> 1; if (r <= mid) { return query(p<<1, l, r, rec, pos); } else if (l > mid) { return query(p<<1|1, l, r, rec, pos); } else { query(p<<1, l, mid, rec, pos); query(p<<1|1, mid+1, r, rec, pos); } } int main() { int pos, rec; while (scanf("%d %d", &N, &K) == 2) { v.clear(); pos = 0; scanf("%s", s + 1); // 从一开始 build(1, 1, N); leave = N - K; // 剩余的要删除的位数 while (leave) { // 如果还有位数需要选择 rec = -1; query(1, pos+1, N-leave+1, rec, pos); v.push_back(rec); --leave; } for (int i = 0; i != (int)v.size(); ++i) { printf("%d", v[i]); } puts(""); } return 0; }