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]);
}