题解 P13524 [KOI 2025 #2] 跳跃
Solution
考虑已知一个排列 \(p\) 怎么推出 \(c\),显然是维护差分标记 \(t\),对于 \(p_i,p_{i+1}\),给 \(t_{\min(p_i,p_{i+1})}\gets t_{\min(p_i,p_{i+1})}+1\) 以及 \(t_{\max(p_i,p_{i+1})}\gets t_{\max(p_i,p_{i+1})}-1\)。
显然可以从 \(c\) 简单推出 \(t\)。下面我们只讨论 \(i\in [2,n-1]\)。由于每个点只会被操作两次(到达一次,出发一次),则只可能存在 \(t_i\in \{-2,0,2\}\),且 \(\sum t_i=0\),即 \(-2\) 的个数 与 \(2\) 的个数相等。
考虑若 \(t_i=-2\),则两次操作这个点,\(i\) 都是作为终点,即 \(i\) 的左右两侧相邻的数都比 \(i\) 小;同理 \(t_i=2\) 表示两侧都比 \(i\) 大;\(t_i=0\) 表示一侧比 \(i\) 大,一侧比 \(i\) 小。
然后你会发现由于 \(c_i\) 为正,\(t_i\) 的每一个前缀和(从 \(2\) 开始)都是非负,所以第一个 \(2\) 一定在第一个 \(-2\) 之前,第二个 \(2\) 一定在第二个 \(-2\) 之前……然后就有一个很好的构造,把 \(1\) 放第一个,再放第一个 \(t_i=-2\) 的 \(i\),放第一个 \(t_i=2\) 的 \(i\),放第二个 \(t_i=-2\) 的 \(i\),以此类推,不难发现一定满足要求。
什么?你问 \(t_i=0\) 怎么办?在任意两个数 \(p_i,p_{i+1}\) 之间插 \([p_i,p_{i+1}]\) 里面(或 \([p_{i+1},p_i]\) 里面)的数就行。随便用 set 搞一下即可。
代码里还缝了一个 checker。
Code
#include<bits/stdc++.h>
using namespace std;
namespace Z3k7223{
const int N=2e5+10;
int n;
int ip[N],a[N];
int ans[N],vis[N],tot;
int tg_chk[N];
void check1(){
for(int i=1;i<n;i++){
int x=ans[i],y=ans[i+1];
if(x>y){
swap(x,y);
}
++tg_chk[x],--tg_chk[y];
}
sort(ans+1,ans+1+n);
for(int i=1;i<=n;i++){
if(ans[i]!=i){
cerr<<"NOT A PERMUTATION!\n";
return;
}
}
for(int i=1;i<n;i++){
tg_chk[i]+=tg_chk[i-1];
}
int cnt=0;
for(int i=1;i<n;i++){
if(tg_chk[i]!=ip[i]){
++cnt;
}
}
cerr<<cnt<<" error(s) found\n";
}
int tag[N];
set<int>s;
void ins(int x){
if(s.empty()){
ans[++tot]=x;
return;
}
vector<int>tmp;
if(ans[tot]>x){
auto it=s.lower_bound(ans[tot]);
while(it!=s.begin()){
--it;
if(*it<x)break;
ans[++tot]=*it;
tmp.push_back(*it);
}
}else{
auto it=s.lower_bound(ans[tot]);
while(it!=s.end()){
if(*it>x)break;
ans[++tot]=*it;
tmp.push_back(*it);
++it;
}
}
for(int r:tmp){
s.erase(r);
}
ans[++tot]=x;
}
void main(){
cin>>n;
for(int i=1;i<n;i++){
cin>>ip[i];
}
for(int i=1;i<=n;i++){
a[i]=ip[i]-ip[i-1];
ans[i]=i;
if(a[i]==0){
s.insert(i);
}
}
vector<int>ar,br;
for(int i=1;i<=n;i++){
if(a[i]==-2){
ar.push_back(i);
}
if(a[i]==2){
br.push_back(i);
}
}
ans[++tot]=1;
for(int i=0;i<ar.size();i++){
ins(ar[i]);
ins(br[i]);
}
for(int i=1;i<=n;i++){
cout<<ans[i]<<' ';
}
cout<<'\n';
check1();
}
}
int main(){
Z3k7223::main();
return 0;
}

浙公网安备 33010602011771号