CF798E. Mike and code of a permutation [拓撲排序 線段樹]

Candy?發表於2017-04-28

CF798E. Mike and code of a permutation

題意:

排列p,編碼了一個序列a。對於每個i,找到第一個\(p_j > p_i\)並且未被標記的j,標記這個j並\(a[i]=j\)。給出a求一個可行的p,保證有解。\(n \le 500000\)


官方題解很詳細

\(b(i) = a^{-1}(i)\),也就是說\(b_i\)表示i被誰標記了

容易想到把小於關係用邊表示然後拓撲排序

將沒有的a和b置為n+1

我們從題目中能直接得到兩種小於關係:\((i,b_i)\),以及\(j \in [1,a_i-1], b_j > i, j \neq i\)

第二種關係可以用線段樹得到

但我們不能遍歷所有邊,否則會退化成\(O(n^2)\)

所以使用dfs式的拓撲排序,dfs到一個節點時直接將他從線段樹中刪除(也就是刪除了他的所有入邊)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
typedef long long ll;
#define mid ((l+r)>>1)
#define lc x<<1
#define rc x<<1|1
#define lson lc, l, mid
#define rson rc, mid+1, r
#define pii pair<int, int>
#define fir first
#define sec second
const int N = 5e5+5;
inline int read(){
    char c=getchar(); int x=0,f=1;
    while(c<'0'||c>'9') {if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9') {x=x*10+c-'0';c=getchar();}
    return x*f;
}

int n, a[N], b[N], vis[N];

namespace S {
    pair<int, int> t[N<<2];
    void build(int x, int l, int r) {
        if(l == r) t[x] = make_pair(b[l], l);
        else {
            build(lson);
            build(rson);
            t[x] = max(t[lc], t[rc]);
        }
    }
    pii que(int x, int l, int r, int ql, int qr) {
        if(ql<=l && r<=qr) return t[x];
        else {
            if(qr <= mid) return que(lson, ql, qr);
            if(mid < ql)  return que(rson, ql, qr);
            return max(que(lson, ql, qr), que(rson, ql, qr));
        }
    }
    void del(int x, int l, int r, int p) {
        if(l == r) t[x] = make_pair(0, l);
        else {
            if(p <= mid) del(lson, p);
            else del(rson, p);
            t[x] = max(t[lc], t[rc]);
        }
    }
}

int q[N], m, p[N];
void dfs(int u) { 
    vis[u] = 1;
    S::del(1, 1, n, u);
    if(b[u] != n+1 && !vis[b[u]]) dfs(b[u]);
    if(a[u] > 1) while(true) {
        pii v = S::que(1, 1, n, 1, a[u]-1);
        if(v.fir > u) dfs(v.sec);
        else break;
    }
    q[++m] = u;
}
int main() {
    //freopen("in", "r", stdin);
    n = read();
    for(int i=1; i<=n; i++) {
        a[i] = read();
        if(a[i] != -1) b[a[i]] = i;
        else a[i] = n+1;
    }
    for(int i=1; i<=n; i++) if(!b[i]) b[i] = n+1;
    S::build(1, 1, n);
    for(int i=1; i<=n; i++) if(!vis[i]) dfs(i);
    m = 0;
    for(int i=1; i<=n; i++) p[q[i]] = ++m;
    for(int i=1; i<=n; i++) printf("%d ", p[i]);
}

相關文章