\(\text{Problem}\)
给定一个长度为 \(N\) 的序列 \(P\) 和一个数 \(K\)
对于满足 \(j-i \ge K and |P_i-P_j|=1\) 一对 \(i,j\) 可交换 \(P_i,P_j\)
求若干次交换后字典序最小的 \(P\)
\(\text{Solution}\)
一道极好的思维题
第一步就想不到。。。
考虑 \(P\) 的逆排列 \(Q\),即令 \(Q[P[i]]=i\)
则发现 \(P\) 中的交换条件在 \(Q\) 中变为相邻的两个数,当数值差大于等于 \(K\) 时即可交换
\(Q\) 中的数值表示 \(P\) 中的位置,\(Q\) 数值对应的下标表示 \(P\) 这个位置填的数
考虑 \(Q\) 中一个数往左交换,即 \(P\) 这个位置往小填数
当在 \(Q\) 中这个数遇到一个与其差小于 \(K\) 的数时,发现无论怎么换也不能换到那个数左边
即在 \(P\) 中这个位置填的数必然大于 \(Q\) 那个数对应到 \(P\) 的位置所填的数
对于 \(Q\) 中的每一个数,往左的与其差小于 \(K\) 的数都如此
那么就可以建个 \(DAG\),表示 \(P\) 位置上填的数的限制关系
回到本题,我们希望位置越小的填的数越小
怎么利用这张图构造最小序列?
一个显然正确的做法是做出原图的反图,每次取出入度为 \(0\) 的最大的编号填上最大的数
好,我们终于完成了解题的思路
遗憾地发现这个做法是 \(O(NK)\) 的
发现 \(DAG\) 中很多边的限制是等效的
根据连边的规则,对于 \(x\),其连向点值域为 \([x-K+1,x]\) 和 \([x,x+K-1]\)
发现 \([x-K+1,x]\) 中的点两两总有边,\([x,x+K-1]\) 也一样,且总是 \(Q\) 中位置靠后的连向位置靠前的
那对于一个 \(x\),我们只需在 \([x-K+1,x]\) 和 \([x,x+K-1]\) 中各找一个最靠后的点连上即可
这样就完成了 \(DAG\) 边的去冗
\(\text{Code}\)
#include <cstdio>
#include <iostream>
#include <queue>
#define ls (p << 1)
#define rs (ls | 1)
#define IN inline
#define RE register
using namespace std;
const int N = 5e5 + 5;
int n, k, p[N], q[N], a[N], seg[N << 4], deg[N], h[N], tot;
priority_queue<int> Q;
struct edge{int to, nxt;}e[N << 1];
IN void add(int x, int y){e[++tot] = edge{y, h[x]}, h[x] = tot;}
IN void read(int &x)
{
x = 0; char ch = getchar();
for(; !isdigit(ch); ch = getchar());
for(; isdigit(ch); x = (x<<3)+(x<<1)+(ch^48), ch = getchar());
}
void Insert(int p, int l, int r, int x, int v)
{
if (l == r) return seg[p] = v, void();
int mid = l + r >> 1;
if (x <= mid) Insert(ls, l, mid, x, v);
else Insert(rs, mid + 1, r, x, v);
seg[p] = max(seg[ls], seg[rs]);
}
int Query(int p, int l, int r, int x, int y)
{
int mid = l + r >> 1;
if (x <= l && r <= y) return seg[p];
int res = 0;
if (x <= mid) res = Query(ls, l, mid, x, y);
if (y > mid) res = max(res, Query(rs, mid + 1, r, x, y));
return res;
}
IN void TopSort()
{
for(RE int i = 1; i <= n; i++) if (!deg[i]) Q.push(i);
int cnt = n;
while (!Q.empty())
{
int x = Q.top(); Q.pop(), a[x] = cnt--;
for(RE int i = h[x]; i; i = e[i].nxt)
if (!(--deg[e[i].to])) Q.push(e[i].to);
}
}
int main()
{
read(n), read(k);
for(RE int i = 1; i <= n; i++) read(p[i]), q[p[i]] = i;
for(RE int i = 1; i <= n; i++)
{
int x = Query(1, 1, n, q[i], min(n, q[i] + k - 1));
if (x) add(q[i], q[x]), ++deg[q[x]];
x = Query(1, 1, n, max(1, q[i] - k + 1), q[i]);
if (x) add(q[i], q[x]), ++deg[q[x]];
Insert(1, 1, n, q[i], i);
}
TopSort();
for(RE int i = 1; i <= n; i++) printf("%d\n", a[i]);
}