前置
第一次独立做出来 *2100
的题,肯定是 cf 评分评高了。
题意简述
有一个排列 p p p,定义 n x t i nxt_i nxti 为 p i p_i pi 右侧第一个大于 p i p_i pi 的位置。若不存在这样的位置则 n x t i = n + 1 nxt_i=n+1 nxti=n+1。
现在排列 p p p 和部分 n x t i nxt_i nxti 丢失了。你需要构造出一个排列 p p p,满足给出的 n x t i nxt_i nxti 关系。 n x t i = − 1 nxt_i=-1 nxti=−1 表示 n x t i nxt_i nxti 丢失(即你可以忽略这个 n x t i nxt_i nxti 关系)。
若无解,输出 -1
。若有多组解,输出任意一组即可。
解题思路
先来考虑什么时候无解。对于一个 i i i,如果存在一个位置 j j j,满足 i < j < n x t i i<j<nxt_i i<j<nxti 且 n x t j > n x t i nxt_j>nxt_i nxtj>nxti,则无解。理解一下,就是说给出的 n x t i nxt_i nxti 会推出大小关系冲突。
再来考虑构造一个合法的解。我们可以将 n x t i = − 1 nxt_i=-1 nxti=−1 的 n x t i nxt_i nxti 全部替换成 i + 1 i+1 i+1。这样的替换一定不会导致有解的序列变成无解。然后我们开一个堆存储当前还未选择的数有哪些,并确立 i i i 和 n x t i nxt_i nxti 的大小关系,从堆中每次选最大的数填就可以了。可以看代码理解一下。
代码示例
#include<bits/stdc++.h>
using namespace std;
#define int long long
int t,n,nxt[500010],st[50][500010],ans[500010];
multiset<int> s;
vector<int> G[500010];
void init(){
s.clear();
for(int i=1;i<=n;i++) G[i].clear();
for(int i=1;i<=n;i++) s.insert(i);
for(int i=1;i<=n;i++) st[0][i]=nxt[i];
for(int i=1;i<=__lg(n);i++){
for(int j=1;j+(1<<i)-1<=n;j++) st[i][j]=max(st[i-1][j],st[i-1][j+(1<<(i-1))]);
}
}
int query(int l,int r){
int len=__lg(r-l+1);
return max(st[len][l],st[len][r-(1<<len)+1]);
}
void solve(){
cin>>n;
for(int i=1;i<=n;i++) cin>>nxt[i];
for(int i=1;i<=n;i++) if(nxt[i]==-1) nxt[i]=i+1;
init();
for(int i=1;i<=n;i++){
if(nxt[i]==i+1) continue;
if(query(i+1,nxt[i]-1)>nxt[i]){
cout<<-1<<endl;
return;
}
}
//st表判断无解情况
for(int i=1;i<=n;i++) G[nxt[i]].push_back(i);
//确立大小关系
for(int i=1;i<=n;i++){
if(nxt[i]==n+1){
ans[i]=*s.rbegin();
s.erase(s.find(*s.rbegin()));
}
}
//初始化 nxt_i=n+1 的位置
for(int i=n;i>=1;i--){
for(int v:G[i]){
ans[v]=*s.rbegin();
s.erase(s.find(*s.rbegin()));
}
//每次找最大的未使用的数填入即可
}
for(int i=1;i<=n;i++) cout<<ans[i]<<" ";
cout<<endl;
return;
}
signed main(){
cin>>t;
while(t--) solve();
return 0;
}