[建图] [线段树] [拓扑排序] 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;
}
posted @ 2026-01-12 20:15  Zwi  阅读(3)  评论(0)    收藏  举报