1. 程式人生 > 其它 >「題解」AGC 001 F Wide Swap

「題解」AGC 001 F Wide Swap

轉化成逆排列(下標和值互換),設其為 \(Q\),那麼操作就變成了如果 \(|Q_i-Q_{i-1}|\geq k\),則可以交換 \(Q_i,Q_{i-1}\),也就是對於任意的 \(i<j,|Q_i-Q_j|<k\)\(Q_i\) 始終要在 \(Q_j\) 前面。

如果欽定了某些元素之間的相對順序,那麼任何滿足這個相對順序的序列都能通過不斷交換兩個相鄰的元素互相得到。

假設要讓 \(A\) 變為 \(B\),現將 \(A\) 中的元素重標號為其在 \(B\) 中的下標,如果其為 \(1\sim n\) 的排列那麼就得到 \(B\) 了。當其不為 \(1\sim n\) 時,一定存在相鄰的逆序對,交換這個逆序對即可。如果不存在逆序對,說明交換得到了 \(B\)

.由於我們每次交換的是逆序對,並且 \(A\)\(B\) 的相對順序限制相同,所以不會存在無法交換的情況。

現在假裝得到了這些限制,想要使得原序列的字典序最小,就需要讓逆排列的 \(1\) 儘可能靠前,在此基礎上 \(2\) 儘可能靠前,然後是 \(3\) 儘可能靠前......

如果只看這些限制,就是菜餚製作這道題了,將 DAG 的反圖建出來之後,拓撲排序時每次選擇編號最大的放在序列的最後。但這道題如果正著建圖並且每次取編號最小的放在最前面也是對的,小粉兔在這裡給了證明我就不另畫蛇添足什麼了(

證明一下為什麼建出 DAG 的反圖然後每次選擇編號最大的放在序列的最後是對的:

考慮如果不將最大的 \(x\)

放在序列的最後,而是放了 \(x'\),由於 \(x\) 是沒有往後移動的限制的,所以將 \(x\) 移動到最後,這樣本在 \(x\) 後且 \(<x\) 的編號都往前移動了一個位置(至少有一個 \(x'\) 會往前移動一個位置),比原先更優,所以任何一種不是 \(x\) 在最後的方案都能如此調整成一個更優的方案,所以 \(x\) 一定在序列的最後。處理好 \(x\) 之後,變成了一個子問題。得證。

實現上,可以用一個線段樹維護原序列的區間還未出隊的元素的最大值,來優化找 \(0\) 度點的過程。

時間複雜度 \(\mathcal{O}(n\log n)\)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<queue>
#define pb emplace_back
#define mp std::make_pair
#define fi first
#define se second
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
typedef std::pair<int, int> pii;
typedef std::vector<int> vi;
const ll mod = 998244353;
ll Add(ll x, ll y) { return (x+y>=mod) ? (x+y-mod) : (x+y); }
ll Mul(ll x, ll y) { return x * y % mod; }
ll Mod(ll x) { return x < 0 ? (x + mod) : (x >= mod ? (x-mod) : x); }
ll cadd(ll &x, ll y) { return x = (x+y>=mod) ? (x+y-mod) : (x+y); }
ll cmul(ll &x, ll y) { return x = x * y % mod; }
template <typename T> T Max(T x, T y) { return x > y ? x : y; }
template<typename T, typename... T2> T Max(T x, T2 ...y) { return Max(x, y...); }
template <typename T> T Min(T x, T y) { return x < y ? x : y; }
template<typename T, typename... T2> T Min(T x, T2 ...y) { return Min(x, y...); }
template <typename T> T cmax(T &x, T y) { return x = x > y ? x : y; }
template <typename T> T cmin(T &x, T y) { return x = x < y ? x : y; }
template <typename T>
T &read(T &r) {
	r = 0; bool w = 0; char ch = getchar();
	while(ch < '0' || ch > '9') w = ch == '-' ? 1 : 0, ch = getchar();
	while(ch >= '0' && ch <= '9') r = r * 10 + (ch ^ 48), ch = getchar();
	return r = w ? -r : r;
}
template<typename T1, typename... T2>
void read(T1 &x, T2& ...y) { read(x); read(y...); }
const int N = 500100;
const int inf = 0x7fffffff;
int n, k, a[N], b[N], id[N], ct;
#define ls tree[x].lson
#define rs tree[x].rson
#define tl tree[x].l
#define tr tree[x].r
int trnt;
struct Node {
	int lson, rson, l, r, mx;
}tree[N << 1];
inline void pushup(int x) { tree[x].mx = Max(tree[ls].mx, tree[rs].mx); }
int build(int l, int r) {
	int x = ++trnt; tl = l; tr = r;
	if(l == r) {
		tree[x].mx = a[l];
		return x;
	}
	int mid = (l + r) >> 1;
	ls = build(l, mid); rs = build(mid+1, r);
	pushup(x);
	return x;
}
int qmax(int x, int l, int r) {
	if(tl >= l && tr <= r) return tree[x].mx;
	int mid = (tl + tr) >> 1, s = -inf;
	if(mid >= l) cmax(s, qmax(ls, l, r));
	if(mid < r) cmax(s, qmax(rs, l, r));
	pushup(x);
	return s;
}
void modify(int x, int p, int v) {
	if(tl == tr) {
		tree[x].mx = v;
		return ;
	}
	int mid = (tl + tr) >> 1;
	if(mid >= p) modify(ls, p, v);
	else modify(rs, p, v);
	pushup(x);
}
#undef ls
#undef rs
#undef tl
#undef tr
std::priority_queue<int>q;
bool check(int i) { return qmax(1, i-k+1, i+k-1) == a[i]; }
signed main() {
	read(n); read(k);
	for(int i = 1; i <= n; ++i) read(a[i]), id[a[i]] = i;
	build(1, n);
	for(int i = 1; i <= n; ++i)
		if(check(i))
			q.push(i);
	ct = n;
	while(!q.empty()) {
		int x = q.top(); q.pop();
		b[ct--] = x;
		modify(1, x, -inf);
		int t = 0;
		t = qmax(1, x-k+1, x);
		if(t != -inf && check(id[t]))
			q.push(id[t]);
		t = qmax(1, x, x+k-1);
		if(t != -inf && check(id[t]))
			q.push(id[t]);
	}
	for(int i = 1; i <= n; ++i) a[b[i]] = i;
	for(int i = 1; i <= n; ++i) printf("%d\n", a[i]);
	return 0;
}