[AGC001F] Wide Swap

发布时间 2023-04-05 11:24:01作者: Crazy!!!

考虑转化对所有能操作得到的\(P\)集合的判定。
\(P\)的逆置换\(Q\)(交换下标和值),操作转化为:若\(|Q_i-Q_{i+1}|\ge K\),可交换\(i\)\(i+1\)
这样转化交换的就是相邻两个位置的值,如果没有前面的限制,任何排列都可以被操作得到。
加上限制,显然有必要条件:数值对\((u,v)\)的位置大小关系不变。实际上这个条件是充分的,任何满足该条件的排列都可以通过从前往后依次交换到对应位置。
这种交换相邻两个值的操作,能构造出的排列很广阔,这也是转化成\(Q\)的意义。
换回\(P\)判定条件变为了:不改变所有\(|i-j|<K\)\(P_i\)\(P_j\)的大小关系。
即若干个不等式限制。考虑拓扑序的最小字典序。
字典序最小等价于优先最大的值填入尽量靠后的位置。
这里值大连小。从大到小考虑每个值填的位置,每次填入存入度为\(0\)位置的优先队列里最大的即可。
不过连边是\(O(nk)\)
入度为\(0\)相当于是\((x-k,x+k)\)里的最大值。线段树维护最大值,一开始扫一遍把入度为\(0\)的加入。
每次填了的位置断边,就在线段树上把这个值赋为\(-inf\),然后分别判\((x-k,x)\)\((x,x+k)\)中的最大值所在位置的入度是否为\(0\)

点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
const int inf = 1e9;
struct seg {int l, r, mx;} T[N << 2];
priority_queue<int> Q;
int n, K, p[N], q[N], ans[N];

void P_up(int x) {T[x].mx = max(T[x << 1].mx, T[x << 1 | 1].mx);}
void Build(int x, int l, int r) {
	T[x].l = l; T[x].r = r;
	if(l == r) {T[x].mx = p[l]; return;}
	int mid = (l + r) >> 1;
	Build(x << 1, l, mid); Build(x << 1 | 1, mid + 1, r);
	P_up(x);
}

void Update(int x, int p) {
	if(T[x].l == T[x].r) {T[x].mx = -inf; return;}
	int mid = (T[x].l + T[x].r) >> 1;
	(p <= mid) ? Update(x << 1, p) : Update(x << 1 | 1, p);
	P_up(x);
}

int Mx(int x, int l, int r) {
	if(l > r) return -inf;
	if(l <= T[x].l && T[x].r <= r) {return T[x].mx;}
	int mid = (T[x].l + T[x].r) >> 1, mx = -inf;
	if(l <= mid) mx = Mx(x << 1, l, r);
	if(r > mid) mx = max(mx, Mx(x << 1 | 1, l, r));
	return mx;
}

bool Ins[N];

void check(int u) {
	if(Ins[u]) return;
	if(Mx(1, max(1, u - K + 1), min(n, u + K - 1)) != p[u]) return;
	Ins[u] = 1; Q.push(u);
}

int main() {
	scanf("%d%d", &n, &K);
	for(int i = 1; i <= n; i++) {scanf("%d", &p[i]); q[p[i]] = i;}
	Build(1, 1, n);
	for(int i = 1; i <= n; i++) {check(i);}
	int v = n;
	while(!Q.empty()) {
		int x = Q.top(); Q.pop(); 
		ans[x] = v--; Update(1, x);
		int wl = Mx(1, max(1, x - K + 1), x - 1), wr = Mx(1, x + 1, min(n, x + K - 1));
		if(wl > -inf) {check(q[wl]);}
		if(wr > -inf) {check(q[wr]);}
	}
	for(int i = 1; i <= n; i++) printf("%d\n", ans[i]);
	return 0;
}