转化成逆排列(下标和值互换),设其为 \(Q\),那么操作就变成了如果 \(|Q_i-Q_{i-1}|\geq k\),则可以交换 \(Q_i,Q_{i-1}\),也就是对于任意的 \(i<j,|Q_i-Q_j|<k\),\(Q_i\) 始终要在 \(Q_j\) 前面。
如果钦定了某些元素之间的相对顺序,那么任何满足这个相对顺序的序列都能通过不断交换两个相邻的元素互相得到。
假设要让 \(A\) 变为 \(B\),现将 \(A\) 中的元素重标号为其在 \(B\) 中的下标,如果其为 \(1\sim n\) 的排列那么就得到 \(B\) 了。当其不为 \(1\sim n\) 时,一定存在相邻的逆序对,交换这个逆序对即可。如果不存在逆序对,说明交换得到了 \(B\).由于我们每次交换的是逆序对,并且 \(A\) 和 \(B\) 的相对顺序限制相同,所以不会存在无法交换的情况。
现在假装得到了这些限制,想要使得原序列的字典序最小,就需要让逆排列的 \(1\) 尽可能靠前,在此基础上 \(2\) 尽可能靠前,然后是 \(3\) 尽可能靠前......
如果只看这些限制,就是菜肴制作这道题了,将 DAG 的反图建出来之后,拓扑排序时每次选择编号最大的放在序列的最后。但这道题如果正着建图并且每次取编号最小的放在最前面也是对的,小粉兔在这里给了证明我就不另画蛇添足什么了(
证明一下为什么建出 DAG 的反图然后每次选择编号最大的放在序列的最后是对的:
考虑如果不将最大的 \(x\) 放在序列的最后,而是放了 \(x'\),由于 \(x\) 是没有往后移动的限制的,所以将 \(x\) 移动到最后,这样本在 \(x\) 后且 \(<x\) 的编号都往前移动了一个位置(至少有一个 \(x'\) 会往前移动一个位置),比原先更优,所以任何一种不是 \(x\) 在最后的方案都能如此调整成一个更优的方案,所以 \(x\) 一定在序列的最后。处理好 \(x\) 之后,变成了一个子问题。得证。
实现上,可以用一个线段树维护原序列的区间还未出队的元素的最大值,来优化找 \(0\) 度点的过程。
时间复杂度 \(\mathcal{O}(n\log n)\).
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<queue>
#define pb emplace_back
#define mp std::make_pair
#define fi first
#define se second
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
typedef std::pair<int, int> pii;
typedef std::vector<int> vi;
const ll mod = 998244353;
ll Add(ll x, ll y) { return (x+y>=mod) ? (x+y-mod) : (x+y); }
ll Mul(ll x, ll y) { return x * y % mod; }
ll Mod(ll x) { return x < 0 ? (x + mod) : (x >= mod ? (x-mod) : x); }
ll cadd(ll &x, ll y) { return x = (x+y>=mod) ? (x+y-mod) : (x+y); }
ll cmul(ll &x, ll y) { return x = x * y % mod; }
template <typename T> T Max(T x, T y) { return x > y ? x : y; }
template<typename T, typename... T2> T Max(T x, T2 ...y) { return Max(x, y...); }
template <typename T> T Min(T x, T y) { return x < y ? x : y; }
template<typename T, typename... T2> T Min(T x, T2 ...y) { return Min(x, y...); }
template <typename T> T cmax(T &x, T y) { return x = x > y ? x : y; }
template <typename T> T cmin(T &x, T y) { return x = x < y ? x : y; }
template <typename T>
T &read(T &r) {
r = 0; bool w = 0; char ch = getchar();
while(ch < '0' || ch > '9') w = ch == '-' ? 1 : 0, ch = getchar();
while(ch >= '0' && ch <= '9') r = r * 10 + (ch ^ 48), ch = getchar();
return r = w ? -r : r;
}
template<typename T1, typename... T2>
void read(T1 &x, T2& ...y) { read(x); read(y...); }
const int N = 500100;
const int inf = 0x7fffffff;
int n, k, a[N], b[N], id[N], ct;
#define ls tree[x].lson
#define rs tree[x].rson
#define tl tree[x].l
#define tr tree[x].r
int trnt;
struct Node {
int lson, rson, l, r, mx;
}tree[N << 1];
inline void pushup(int x) { tree[x].mx = Max(tree[ls].mx, tree[rs].mx); }
int build(int l, int r) {
int x = ++trnt; tl = l; tr = r;
if(l == r) {
tree[x].mx = a[l];
return x;
}
int mid = (l + r) >> 1;
ls = build(l, mid); rs = build(mid+1, r);
pushup(x);
return x;
}
int qmax(int x, int l, int r) {
if(tl >= l && tr <= r) return tree[x].mx;
int mid = (tl + tr) >> 1, s = -inf;
if(mid >= l) cmax(s, qmax(ls, l, r));
if(mid < r) cmax(s, qmax(rs, l, r));
pushup(x);
return s;
}
void modify(int x, int p, int v) {
if(tl == tr) {
tree[x].mx = v;
return ;
}
int mid = (tl + tr) >> 1;
if(mid >= p) modify(ls, p, v);
else modify(rs, p, v);
pushup(x);
}
#undef ls
#undef rs
#undef tl
#undef tr
std::priority_queue<int>q;
bool check(int i) { return qmax(1, i-k+1, i+k-1) == a[i]; }
signed main() {
read(n); read(k);
for(int i = 1; i <= n; ++i) read(a[i]), id[a[i]] = i;
build(1, n);
for(int i = 1; i <= n; ++i)
if(check(i))
q.push(i);
ct = n;
while(!q.empty()) {
int x = q.top(); q.pop();
b[ct--] = x;
modify(1, x, -inf);
int t = 0;
t = qmax(1, x-k+1, x);
if(t != -inf && check(id[t]))
q.push(id[t]);
t = qmax(1, x, x+k-1);
if(t != -inf && check(id[t]))
q.push(id[t]);
}
for(int i = 1; i <= n; ++i) a[b[i]] = i;
for(int i = 1; i <= n; ++i) printf("%d\n", a[i]);
return 0;
}