题解 AT1984 【[AGC001F] Wide Swap】
在博客园食用更佳:https://www.cnblogs.com/PinkRabbit/p/AGC001F.html。
题意简述
有一个
- 选取两个下标
i, j (1 \le i < j \le N ),还需满足j - i \ge K 且|P_i - P_j| = 1 ,然后交换P_i 与P_j 的值。
请问你能得到的字典序最小的排列是什么?请输出它。
题解
它给了个排列
这个条件看起来十分的玄妙,考虑
看起来舒服多了。我们需要求出字典序最小的
然后再考虑
也就是说:对于所有满足
而对于两个排列
也就是说只要求出满足所有
对于所有满足
你可以回去观察一下样例是否满足这个条件,一定是满足的。
然而此时我们需要让
可以发现我们把不等号写出来后,整个序列就变成了个 DAG,我们要给予每个点适当的拓扑编号,让拓扑编号的字典序尽量小。
这就变成了一个经典问题。在一般情况下,它的解决方案并不是部分题解所述的:「直接拓扑排序,但每次优先取编号最小的点」。
而是:把所有边反向,然后拓扑排序(也就是倒着拓扑排序),但每次优先取编号最大的点,拓扑编号也从
这两种方法是对称的,但是在一般情况下求得的东西并不相同,且第二种才是对的。
无论如何,这张图的边数还是
任意时刻下,入度为
删除一个点
那么我们可以用线段树维护这个过程,初始时先查一遍每个点是否入度为
然后每次取出堆顶,删除它然后分别查询
最后直接输出编号即可,很有趣的一题,时间复杂度为
最后:之所以前文中第一种「错误」的方法也能 AC,是因为本题中特殊的连边形式:
考虑第一种方法第一次错误编号时:假设是把本应给位置
现在
注意到此时
一直沿 DAG 中的边往回走(删除已编号的点),最终会走到没有入度的点,如果不是
所以一定有:最终答案中,此时未编号的,比
所以此时我们如果直接把
与
#include <cstdio>
#include <algorithm>
#include <queue>
const int Inf = 0x3f3f3f3f;
const int MN = 500005, MS = 1 << 20 | 7;
int N, K, P[MN], Ans[MN];
#define li (i << 1)
#define ri (li | 1)
#define mid ((l + r) >> 1)
#define ls li, l, mid
#define rs ri, mid + 1, r
int mxp[MS];
void Build(int i, int l, int r) {
if (l == r) return mxp[i] = l, void();
Build(ls), Build(rs);
mxp[i] = P[mxp[li]] > P[mxp[ri]] ? mxp[li] : mxp[ri];
}
void Del(int i, int l, int r, int p) {
if (l == r) return mxp[i] = 0, void();
p <= mid ? Del(ls, p) : Del(rs, p);
mxp[i] = P[mxp[li]] > P[mxp[ri]] ? mxp[li] : mxp[ri];
}
int Qur(int i, int l, int r, int a, int b) {
if (r < a || b < l) return 0;
if (a <= l && r <= b) return mxp[i];
int v1 = Qur(ls, a, b), v2 = Qur(rs, a, b);
return P[v1] > P[v2] ? v1 : v2;
}
int inq[MN];
std::priority_queue<int> pq;
inline void check(int id) {
if (inq[id]) return ;
if (Qur(1, 1, N, id - K + 1, id + K - 1) == id)
pq.push(id), inq[id] = 1;
}
int main() {
scanf("%d%d", &N, &K);
for (int i = 1; i <= N; ++i) scanf("%d", &P[i]);
P[0] = -Inf;
Build(1, 1, N);
for (int i = 1; i <= N; ++i) check(i);
for (int i = N; i >= 1; --i) {
int u = pq.top(); pq.pop();
Ans[u] = i;
Del(1, 1, N, u);
int pos;
if ((pos = Qur(1, 1, N, u - K + 1, u - 1))) check(pos);
if ((pos = Qur(1, 1, N, u + 1, u + K - 1))) check(pos);
}
for (int i = 1; i <= N; ++i) printf("%d\n", Ans[i]);
return 0;
}