Part.1 有关本题
本蒟蒻想起这道巧妙 又毒瘤 的题,到处搜寻提交窗口。好不容易找到窗口,有花了
3
h
3h
3h 的时间调题。
本蒟蒻为了悲剧不再发生,于是出了这道题,有写下了这篇题解以供后人。
以下的题解默认以阅读过原题。
Part.2 前置芝士
Part.2-1 DFS序列
DFS序列就是先序遍历的别称,一课子树DFS序列连续
Part.3 题意
言归正传,开始讲题。首先我们说思路,题面中说有一只小羊,它拥有超能力改变某棵二叉树一个节点的父亲。它问最先序遍历最小的字典序。
Part.4 题解
Part.4-1 n^3 暴力
我们枚举一个节点 u u u,新父亲 F F F,将原父亲 f f f 连向 u u u 的边去掉,将 F F F 的一个儿子变为 u u u 即可。更改完毕后对它跑DFS生成答案,取最小字典序。
Part.4-2 转移思路
使用如上所述的DFS序列解题。
红是原来的DFS序列,黄操作可以转换为将
[
8
,
10
]
[8,10]
[8,10] 区间(
8
8
8 的子树)前移至
6
6
6 后,绿操作将
[
9
,
9
]
[9,9]
[9,9] 区间后置到
11
11
11 后,紫操作强调了可以放在最后。
但是可以发现,自己和自己的左孩子之间不可以插入区间。
Part.4-4 贪心
考虑前移,那么我们假设有一段区间移动到了 i i i 位置后,那么原来后面的是 i + 1 i+1 i+1,现在变成了某位置 j j j。我们贪心让 j j j 位置小,此时我们移动来了区间 [ j , j + s z j − 1 ] [j,j+sz_j-1] [j,j+szj−1]( s z sz sz 表示子树大小)。
后置区间 i + 1 i+1 i+1 位置及其子树,那么原来 i i i 位置之后是 i + 1 i+1 i+1,现在变成了 i + 1 + s z i + 1 i+1+sz_{i+1} i+1+szi+1。它的判断是 O ( 1 ) O(1) O(1) 的,可以先判断完。
如果一个为后置的区间 [ l , l + s z l − 1 ] [l,l+sz_l-1] [l,l+szl−1] 找到位置,假设插到了 k k k 位置前,那么 k k k 是满足 k k k 位置的值大于 l l l 且它前面可插数的第一个位置。
预处理前面需要的最小值和能否可以插数即可。
Part.5 代码
#include <bits/stdc++.h>
using namespace std;
#define N 100010
int mn[N],id[N],ls[N],rs[N],sz[N],can[N],tot;
void dfs(int u){
id[++tot]=u; sz[u]=1;
if (ls[u]) dfs(ls[u]),sz[u]+=sz[ls[u]];
if (rs[u]) dfs(rs[u]),sz[u]+=sz[rs[u]];
}void out(int l,int r){
for (int i=l; i<=r; i++) cout<<id[i]<<" ";
}int main(){
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
int t; cin>>t;
while (t--){
int n; cin>>n; tot=0; mn[n+1]=1000000;
for (int i=1; i<=n; i++) cin>>ls[i]>>rs[i];
dfs(1); int v1,v2,v3,tp=0;
for (int i=1; i<=n; i++) can[i]=!ls[id[i]];
for (int i=n; i>=1; i--) mn[i]=min(mn[i+1],id[i]);
for (int i=1; i<=n; i++){
if (mn[i+1]<id[i+1]&&can[i]){
int j=i+1; //cout<<"::"<<i<<" "<<id[i+1]<<" "<<mn[i+1]<<"\n";
for (;j<=n; j++) if(id[j]==mn[i+1]) break;
v1=i+1,v2=j,v3=j+sz[id[j]]; tp=1;
//cout<<":::"<<v1<<" "<<v2<<" "<<v3<<"\n";
break;
}
}for (int i=2; i<=n; i++){
//cout<<":::::"<<sz[id[i]]<<"\n";
if (i+sz[id[i]]<=n&&id[i+sz[id[i]]]<id[i]&&(i<v1||i==v1&&id[i+sz[id[i]]]<=id[v2]||tp==0)){
//cout<<"::"<<i<<" "<<i+sz[id[i]]<<"\n";
v1=i; v2=i+sz[id[i]]; tp=2; v3=n+1; break;
}
}if (tp==0) out(1,n);
else{
if (tp==2){
for (int j=v2+1; j<=n; j++){
if (can[j-1]&&id[j]>id[v1]){
v3=j; break;
}
}
}out(1,v1-1); out(v2,v3-1);
out(v1,v2-1); out(v3,n);
//cout<<"\n";
//cout<<"::"<<v1<<" "<<v2<<" "<<v3<<" "<<tp;
}cout<<"\n";
}
}