非常好题目。做完之后感觉,自己归纳性质的能力需要加强,不能让思考仅仅停留在手玩和感性理解,要尝试将一些东西往已知的模型上想。
设 在树原来的 dfs 序为位置
。
手玩一下操作过程,发现操作可以分为若干个阶段,每个阶段都是将数
 从根换到下面,并把  换到根。
换  的时候,它会沿着 
最小的一条链一路换下去,最后换到某个叶子。事实上,这个叶子就是 DFS
过程中,第一个出栈的节点。
换完之后,除这个叶子,剩下的  个节点上的数仍然按原来的 DFS
序排列,也就是说,这个过程可以归纳地进行下去。
那我们就知道树上的数在很多次操作后的形状了。每个节点上的数将从 DFS
序变为出栈序。
而且,每次将数 
恰好换到其对应位置的时候,数  所在的节点的出栈序是  ,而数  在剩下的节点中,按 DFS
序的顺序排列。
再次观察操作过程,发现任何时候,儿子之间  的相对大小关系是不变的。
证明:
考虑点  和它的儿子  ,。
一次关于  的交换,  显然会找到第一个  的儿子
。所以有 。
那么将  替换为 ,相对大小关系肯定不变。
而如果还要交换 
与其子节点,显然  仍然成立,同时,
不是这个操作阶段正在下移的数的目标节点。
所以,在交换之后,  的值,在
这个操作阶段结束前,值都不会改变,所以它们仍然按原图的
DFS 序排列,所以它们的相对大小关系不变。
那么  就直接可以通过将  排序后 DFS 来产生,所以题面中的“多解输出任意解”是假的。
回到原问题。
考虑先算出当前在哪个操作阶段。如果 ,说明此时未进行任何操作,否则当前阶段数就是 。(即数  正在从上往下移动)。令 。
算出来之后,手动回退一部分操作,使得当前局面为该阶段的开头。
接下来检查合法性,我们需要确保  都换到了出栈序为  的节点,同时 
按原来的 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
   | #include <bits/stdc++.h> using namespace std; const int N = 3e5 + 5; int n; int a[N]; int fir[N],nxt[N << 1],to[N << 1],ect = 1; inline void addedge(int u1,int v1) { 	nxt[++ect] = fir[u1];fir[u1] = ect;to[ect] = v1; } vector<int> sons[N]; int dep[N],fa[N]; int pos[N]; int frt[N];  int ext[N];  long long ans; void dfs0(int x,int f)  { 	for(int i = fir[x],y;y = to[i],i;i = nxt[i]) { 		if(y == f) continue; 		dep[y] = dep[x] + 1; 		fa[y] = x; 		dfs0(y,x); 		sons[x].push_back(y); 	} 	sort(sons[x].begin(),sons[x].end(),[&](const int &x,const int &y) { return a[x] < a[y];}); } int ps_frt[N],ps_ext[N]; void dfs1(int x) { 	frt[x] = ++frt[0];ps_frt[frt[x]] = x; 	for(auto y : sons[x]) dfs1(y); 	ext[x] = ++ext[0];ps_ext[ext[x]] = x; } bool Isanc(int u,int v) {  	if(u == v) return true; 	if(u == 1) return false; 	return Isanc(fa[u],v); } void GoBack(int u) { 	if(u == 1) return; 	swap(a[u],a[fa[u]]); 	++ans; 	GoBack(fa[u]); }  int main() { 	cin >> n; 	for(int i = 1;i <= n;i++) cin >> a[i]; 	for(int i = 1,u,v;i < n;i++) 		cin >> u >> v,addedge(u,v),addedge(v,u); 	dfs0(1,0); 	dfs1(1); 	if(a[1] == 1) { 		for(int i = 1;i <= n;i++) 			if(frt[i] != a[i]) return puts("NO"),0; 		puts("YES"); 		puts("0"); 		for(int i = 1;i <= n;i++) printf("%d ",frt[i]);printf("\n"); 		return 0; 	} 	int cur = a[1] - 1;  	for(int i = 1;i <= n;i++) pos[a[i]] = i; 	if(!Isanc(ps_ext[cur],pos[cur])) return puts("NO"),0;  	GoBack(pos[cur]); 	for(int i = 1;i <= n;i++) pos[a[i]] = i; 	for(int i = 1;i < cur;i++) 		if(ext[pos[i]] != i) return puts("NO"),0; 	for(int i = cur;i < n;i++) 		if(frt[pos[i]] > frt[pos[i + 1]]) return puts("NO"),0; 	puts("YES"); 	for(int i = 1;i < cur;i++) ans += dep[pos[i]]; 	printf("%lld\n",ans); 	for(int i = 1;i <= n;i++) printf("%d ",frt[i]);printf("\n"); 	return 0; }
 
  |