[做题记录-乱做] [AGC001F] Wide Swap

题意

给出一个元素集合为\(\{1,2,\dots,N\}\) \((1\leq N\leq 500,000)\)的排列\(P\),当有\(i,j\) \((1\leq i<j\leq N)\)满足\(j-i\geq K\) \((1\leq K\leq N-1)\)\(|P_{i}-P_{j}|==1\)时,可以交换\(P_{i}\)\(P_{j}\)

求:可能排列中字典序最小的排列

\(2 \leq N \leq 500,000\)

题解

看到\(|P_i - P_j| = 1\)这种东东考虑先搞一个新排列\(Q_{P_i} = i\), 可以发现, 如果\(Q\)的字典序最小, 那么\(P\)的字典序最小。

考虑一下我们的条件变成了什么, 就是相邻的位置的绝对值大于等于\(K\)时候,我们可以交换这两个位置上的数。

推广一下限制, 也就是说\(i < j, |Q_i - Q_j| <K\)的时候需要连一条边\((Q_i\rightarrow Q_j)\)表示 \(Q_i\)必须在\(Q_j\)以前出现。

那么这样会搞出\(n^2\)条边来, 然后剩下的就是菜肴制作那个题了。

然后考虑优化, 可以发现我们拓扑排序只需要知道一个点是否有入度。把边反向以后, 使用一个值域线段树维护,每次在\([x - K + 1, x + K - 1]\)中查最小值/最大值是否小于当前点\(x\), 然后跑拓扑就是了。

还有一个做法是考虑优化连边主要是写起来难受。显然关系有传递性的, 所以找到满足条件的离\(i\)最靠近的点连边即可。

这里是后面一个的代码。

/*
	QiuQiu /qq
  ____    _           _                 __                
  / __ \  (_)         | |               / /                
 | |  | |  _   _   _  | |  _   _       / /    __ _    __ _ 
 | |  | | | | | | | | | | | | | |     / /    / _` |  / _` |
 | |__| | | | | |_| | | | | |_| |    / /    | (_| | | (_| |
  \___\_\ |_|  \__,_| |_|  \__, |   /_/      \__, |  \__, |
                            __/ |               | |     | |
                           |___/                |_|     |_|
*/

#include <bits/stdc++.h>

using namespace std;

using ll = long long;
using pii = pair<int, int>;

namespace Mod {

	const int P = 998244353;

	inline int mod(int x) { return x + ((x >> 31) & P); }
	inline void pls(int &x, int y) { x = mod(x + y - P); }
	inline void dec(int &x, int y) { x = mod(x - y); }

	inline int power(int x, int k) {
		int res = 1;
		while(k) { if(k & 1) res = 1ll * res * x % P; x = 1ll * x * x % P; k >>= 1; } return res;
	}

	const int inv2 = power(2, P - 2);

}

const int N = 5e5 + 5;
const int INF = 0x3f3f3f3f;

int n, K;
int Q[N], P[N];

#define ls(x) (x << 1)
#define rs(x) (x << 1 | 1)

int mx[N << 2];

int qry(int x, int l, int r, int L, int R) {
	if(L <= l && r <= R) return mx[x];
	int mid = (l + r) >> 1;
	if(R <= mid) return qry(ls(x), l, mid, L, R);
	if(L > mid) return qry(rs(x), mid + 1, r, L, R);
	return max(qry(ls(x), l, mid, L, R), qry(rs(x), mid + 1, r, L, R));
}

void mdf(int x, int l, int r, int p, int v) {
	if(l == r) { mx[x] = v; return ; }
	int mid = (l + r) >> 1;
	if(p <= mid) mdf(ls(x), l, mid, p, v);
	else mdf(rs(x), mid + 1, r, p, v);
	mx[x] = max(mx[ls(x)], mx[rs(x)]);
}

vector<int> e[N];
int out[N], deg[N];

int main() {
	ios :: sync_with_stdio(false);
	cin >> n >> K;
	for(int i = 1; i <= n; i ++) {
		cin >> P[i]; Q[P[i]] = i;
	}
//	for(int i = 1; i <= n; i ++) cerr << Q[i] << ' ' ; 
	for(int i = 1; i <= n; i ++) {
		int k = qry(1, 1, n, max(1, Q[i] - K + 1), Q[i]);

		if(k) e[Q[i]].push_back(Q[k]), deg[Q[k]] ++;

		k = qry(1, 1, n, Q[i], min(Q[i] + K - 1, n));

		if(k) e[Q[i]].push_back(Q[k]), deg[Q[k]] ++;

		mdf(1, 1, n, Q[i], i);
		//cout << k << endl;
	}
	priority_queue<int> q;
	for(int i = 1; i <= n; i ++) if(! deg[i]) {
		q.push(i);
	//	cerr << i << endl;
	}
	int tac = n;
	while(q.size()) {
		int x = q.top(); q.pop();
		//cerr << x << ' ' << out[x] << endl;
		out[x] = tac --; //tac --;
		for(int y : e[x]) {
			deg[y] --;
			if(deg[y] == 0) q.push(y); 
		}
	}
	for(int i = 1; i <= n; i ++) cout << out[i] << endl;
	return 0;
}	
posted @ 2021-09-29 22:31  HN-wrp  阅读(50)  评论(0编辑  收藏  举报