Atcoder 题解

[题目链接]

https://atcoder.jp/contests/agc001/tasks/agc001_f

[题解]

题目中的限制比较麻烦 , 首先转化问题 , 考虑排列的逆 \(Q = P ^ {-1}\)

那么 , \(Q\) 中相邻元素差不超过 \(K\) 的都可以任意互换。

考虑两个元素差值 \(|u - v| < K\) , 根据上面所述 , \(u\)\(v\)\(Q\) 中的相对顺序永远不会改变。

一个重要的事实是 : 设一个新序列 \(R\) , 只要 \(Q , R\) 相对顺序关系不改变 , 那么就必然存在一个有限步的操作序列 , 使 \(Q\) 转化为 \(R\)。 这是因为必然能在 \(Q\) 中找到相邻两个元素 , 交换后使逆序对数减少了 \(1\)

将这个条件对应到 \(P\) 上 , 原限制转化为对于 \(|i - j| \leq K , P_{i} \leq P_{j}\) , 必然有 \(i\) 的值比 \(j\) 的值小。 反之亦然。

所有这样的关系可以构成一张有向无环图 (\(DAG\)) , 要求的就是求出一个拓扑序列将节点编号 , 并使得字典序尽可能小。

注意到出度为 \(0\) 的点中最大编号点可以直接填当前的最大值 , 那么用堆维护点做一遍反向拓扑排序即可。 时间复杂度 \(O(N ^ 2)\)

这样仍然不能通过本题。 考虑用数据结构优化这个过程。

首先建一棵线段树维护 \(P\) 值。

对于一个点 \(i\) , 若 \((i - K , i + K)\) 中的最大值是 \(P_{i}\) , 那么这个点入队。

每次从堆中弹出一个点 , 相当于将它的权值赋为 \(-\infty\)。 那么从两边找到新的出度为 \(0\) 的点 , 继续执行上述过程即可。

时间复杂度 : \(O(NlogN)\)

[代码]

#include <bits/stdc++.h>

using namespace std;

typedef long long LL;

const int MN = 5e5 + 5 , INF = 2e9;

int N , K , P[MN] , mxp[MN << 2] , Ans[MN] , inq[MN];
priority_queue < int > pq;

inline void build(int now , int l , int r) {
	if (l == r) { mxp[now] = l; return; }
	int mid = l + r >> 1;
	build(now << 1 , l , mid) , build(now << 1 | 1 , mid + 1 , r);
	mxp[now] = P[mxp[now << 1]] > P[mxp[now << 1 | 1]] ? mxp[now << 1] : mxp[now << 1 | 1];
}
inline int Query(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 mid = l + r >> 1;
	int v1 = Query(i << 1, l , mid , a, b), v2 = Query(i << 1 | 1, mid + 1 , r , a, b);
	return P[v1] > P[v2] ? v1 : v2;
}
inline void Del(int now , int l , int r , int x) {
	if (l == r) { mxp[now] = 0; return; }
	int mid = l + r >> 1;
	if (mid >= x) Del(now << 1 , l , mid , x);
	else Del(now << 1 | 1 , mid + 1 , r , x);
	mxp[now] = P[mxp[now << 1]] > P[mxp[now << 1 | 1]] ? mxp[now << 1] : mxp[now << 1 | 1];
} 
inline void check(int id) {
	if (inq[id]) return;
	if (Query(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 = Query(1 , 1 , N , u - K + 1 , u - 1))) check(pos);
		if ((pos = Query(1 , 1 , N , u + 1 , u + K - 1))) check(pos);
	}
	for (int i = 1; i <= N; ++i) printf("%d\n" , Ans[i]);
	return 0;
}