这题难度1700,我感觉又小了……
这题虽然没几个人是用kruskal重构树的思想做的,但是我是,所以我就放了个kruskal重构树的标签。
题目链接:CF原网
题目大意:有一个长为 $n$ 的排列,一开始每个数都是一个独立的联通块。有 $n-1$ 次操作,每次要求 $x_i$ 和 $y_i$ 所在的联通块相邻,然后把这两个联通块合并。求一个合法的排列使得所有操作合法。保证有解。
$1le nle 1.5 imes 10^5$。
这题我想了想,就想到了kruskal重构树。(smg?)
kruskal重构树实际上是保证了对于原图的任意一个联通导出子图,如果其中的边都满足“最小”“最大”这类限制,那么在重构树上必有一棵子树满足包含的叶子结点有且仅有导出子图的点。
那么这题也类似,对于时间小的操作涉及到的点都在一个子树上。
那就直接kruskal重构树好了。最后答案是这个重构树的只包含叶子编号的前序遍历。(后序遍历也行)
时间复杂度 $O(nlog n)$。
代码短的惊人。
#include<bits/stdc++.h> using namespace std; const int maxn=300030; #define FOR(i,a,b) for(int i=(a);i<=(b);i++) inline int read(){ char ch=getchar();int x=0,f=0; while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar(); while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar(); return f?-x:x; } int n,cnt,ls[maxn],rs[maxn],fa[maxn]; int getfa(int x){ return x==fa[x]?x:fa[x]=getfa(fa[x]); } void dfs(int x){ if(ls[x]) dfs(ls[x]); if(x<=n) printf("%d ",x); if(rs[x]) dfs(rs[x]); } int main(){ n=cnt=read(); FOR(i,1,2*n) fa[i]=i; FOR(i,1,n-1){ int x=read(),y=read(); int u=getfa(x),v=getfa(y); ls[++cnt]=u;rs[cnt]=v; fa[u]=fa[v]=cnt; } dfs(cnt); }