[建图] [线段树] [拓扑排序] AT_agc001_f [AGC001F] Wide Swap
posted on 2025-05-30 02:03:54 | under | source
题意:给你一个排列 \(p\),对于 \(i,j\) 满足 \(|i-j|\ge k\) 且 \(|p_i-p_j|=1\) 可以交换 \(p_i,p_j\)。最小化 \(p\) 的字典序。\(k\le n\le 5\times 10^5\)。
转置一下条件,那么放在 \(p^{-1}\) 上就是当差值 \(\ge k\) 时便能交换邻项。然后就有经典推论:最终状态可以到达,当且仅当所有满足差值 \(<k\) 的数对的相对位置不变。
回到原排列,就相当于若满足 \(|i-j|<k\) 则最终 \(p_i,p_j\) 的大小关系不能变。
容易想到建图,用一条有向边 \(i\to j\) 表示 \(p_i<p_j\),那么就要最小化拓扑编号。这并非拓扑序,正确的做法其实是求反图的最大拓扑序再翻转。正确性基于每次将较大元素向右移更优。
不能直接做,考虑优化。发现 \(x\) 入度为零等价于 \(p_x=\max\limits_{x-k<i<x+k}p_i\)。用线段树维护即可,每次删去队首元素并更新,然后检查左右两半区间的最大值是否入度为零。
复杂度 \(O(n\log n)\)。
代码
#include<bits/stdc++.h>
using namespace std;
const int N = 5e5 + 5, inf = 1e9;
int n, k, a[N], L[N], R[N], ans[N], _n;
bool vis[N];
namespace Sg_Tree{
#define lt (u << 1)
#define rt (u << 1 | 1)
#define mid (l + r >> 1)
int t[N << 2];
inline int mer(int x, int y) {return a[x] > a[y] ? x : y;}
inline void psup(int u) {t[u] = mer(t[lt], t[rt]);}
inline void build(int u, int l, int r){
if(l == r) {t[u] = l; return ;}
build(lt, l, mid), build(rt, mid + 1, r);
psup(u);
}
inline void upd(int u, int l, int r, int k){
if(l == r) return ;
if(k <= mid) upd(lt, l, mid, k);
else upd(rt, mid + 1, r, k);
psup(u);
}
inline int fid(int u, int l, int r, int ll, int rr){
if(ll <= l && r <= rr) return t[u];
if(ll <= mid && rr > mid) return mer(fid(lt, l, mid, ll, rr), fid(rt, mid + 1, r, ll, rr));
if(ll <= mid) return fid(lt, l, mid, ll, rr);
return fid(rt, mid + 1, r, ll, rr);
}
}using namespace Sg_Tree;
inline bool chk(int x){
return !vis[x] && (fid(1, 1, n, L[x], R[x]) == x);
}
signed main(){
cin >> n >> k;
for(int i = 1; i <= n; ++i) scanf("%d", &a[i]), L[i] = max(1, i - k + 1), R[i] = min(n, i + k - 1);
build(1, 1, n);
priority_queue<int> q;
for(int i = 1; i <= n; ++i) if(chk(i)) vis[i] = true, q.push(i);
_n = n;
while(!q.empty()){
int u = q.top(); q.pop();
ans[u] = _n--, a[u] = -inf, upd(1, 1, n, u);
if(L[u] < u){
int ls = fid(1, 1, n, L[u], u - 1);
if(chk(ls)) vis[ls] = true, q.push(ls);
}
if(u < R[u]){
int rs = fid(1, 1, n, u + 1, R[u]);
if(chk(rs)) vis[rs] = true, q.push(rs);
}
}
for(int i = 1; i <= n; ++i) printf("%d\n", ans[i]);
return 0;
}

浙公网安备 33010602011771号