Codeforces1508D - Tree Calendar

Codeforces1508D - Tree Calendar

题目大意:

有一棵已知的有根树和一个未知的 \(\text{dfs}\) 序,且做了若干次操作,每次操作是

对于所有的 \((u,fa_u)\and label_{fa_u}<label_{u}\) ,找到最小的二元组 \((label_{fa_u},lable_u)\) ,交换二元组的 \(label\)

给定最终的序列,求复原一个 \(\text{dfs}\) 序并且给出操作次数,或者确定不存在这样的 \(\text{dfs}\)

\[ \ \]

模拟这样的过程,容易发现:

1号节点沿着最小 \(\text{dfs}\) 序路径走下去,直到叶子,同时将路径上的点推上来,一共推了 \(dep_{leaf}\)

2号节点沿着最小 \(\text{dfs}\) 序路径走下去,直到叶子,同时将路径上的点推上来,一共推了 \(dep_{leaf'}\)

....

考虑每个节点都已经推到最底下的情况,则最终所有的节点有两种情况

1.是推下来的节点,则其 \(label\) 恰好为原树上出栈序列的标号

2.剩下的点构成一个新的连通块,按照新的 \(\text{dfs}\) 序的顺序标号

那么考虑找到当前最小的二元组 \((label_{fa_u},label_u)\) ,就知道当前正在推的是哪个元素

考虑先复原这个元素被推下来的过程,复原的过程中注意判定是否当前的元组合法

然后容易通过当前的 \(label\) 确定一开始的 \(\text{dfs}\)

具体的,设 \(s_u\)\(u\) 子树中最小的 \(label\) ,按照 \(s_u\) 递增的顺序遍历每个儿子得到的 \(\text{dfs}\) 才可能是合法的 \(\text{dfs}\)

原理比较显然,已经被推的叶子按照 \(\text{stack}\) 序遍历,剩下的按照原先的 \(\text{dfs}\) 序遍历,最终取 \(\text{min}\) 然后遍历即合法

然后按照上面的过程,对于得到的 \(\text{dfs}\) 序判定是否合法即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef pair <int,int> Pii;
#define reg register
#define mp make_pair
#define pb push_back
#define Mod1(x) ((x>=P)&&(x-=P))
#define Mod2(x) ((x<0)&&(x+=P))
#define rep(i,a,b) for(int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(int i=a,i##end=b;i>=i##end;--i)
template <class T> inline void cmin(T &a,T b){ ((a>b)&&(a=b)); }
template <class T> inline void cmax(T &a,T b){ ((a<b)&&(a=b)); }

char IO;
template <class T=int> T rd(){
T s=0; int f=0;
while(!isdigit(IO=getchar())) f|=IO=='-';
do s=(s<<1)+(s<<3)+(IO^'0');
while(isdigit(IO=getchar()));
return f?-s:s;
}

const int N=3e5+10;

int n;
int A[N],F[N];
vector <int> G[N];
ll ans;
int X[N],Y[N],Z[N],C1,C2,C3,D[N];
// X 原树 dfs 序
// y 原树 stack 序
// z 删去已经推完的点之后,剩下的点的 dfs 序

int I[N],J[N];
void dfs1(int u){
I[u]=A[u];
for(int v:G[u]) D[v]=D[u]+1,dfs1(v),cmin(I[u],I[v]);
}
void dfs2(int u){
J[X[u]=++C1]=u;
sort(G[u].begin(),G[u].end(),[&](int x,int y){ return I[x]<I[y]; });
for(int v:G[u]) dfs2(v);
Y[u]=++C2;
}
int vis[N];
void dfs3(int u){
if(vis[u]) return;
Z[u]=++C3;
for(int v:G[u]) dfs3(v);
}

int main(){
n=rd();
rep(i,1,n) A[i]=rd();
int p=n+1; A[n+1]=n+1;
rep(i,2,n) {
int u=rd(),v=rd();
G[u].pb(v),F[v]=u;
if(A[u]<A[v] && A[p]>A[u]) p=u;
}
int f=1;
if(p<=n) while(F[p]) {
int f=F[p];
// illgal swap
if(A[f]<A[p]) return puts("NO"),0;
swap(A[p],A[F[p]]);

// not optimal swap
if(F[f] && A[F[f]]<A[f]) return puts("NO"),0;
for(int v:G[f]) if(A[v]>A[f] && A[v]<A[p]) return puts("NO"),0;
p=F[p],ans++;
}
dfs1(1),dfs2(1);
rep(i,1,n) if(A[i]<A[p]) f&=A[i]==Y[i],vis[i]=1,ans+=D[i];
dfs3(1);
rep(i,1,n) if(A[i]>=A[p]) f&=Z[i]+A[p]-1==A[i];
if(!f) puts("NO");
else {
puts("YES");
printf("%lld\n",ans);
rep(i,1,n) printf("%d ",X[i]);
puts("");
}
}