CF798E. Mike and code of a permutation [拓扑排序 线段树]

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]);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值