非常好题目。做完之后感觉,自己归纳性质的能力需要加强,不能让思考仅仅停留在手玩和感性理解,要尝试将一些东西往已知的模型上想。
设 在树原来的 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; }
|