Atcoder题解:Arc156_c
数据范围 \(5\cdot 10^3\),但是介绍一个 \(O(n\log n)\) 做法。
我们考虑观察样例,发现样例都很小,而且 \(\text{LCS}\) 的长度都是 \(1\),那么我们就猜答案最多为 \(1\),并尝试去构造。
我们画一个链,发现链上的统配解就是倒过来。那么我们考虑普通的树,我们发现,普通的树比链的性质更强一些。
我们可以找到树的重心,将其分割成若干个子树。然后安排每个子树中的值到别的地方去。这样可以保证每个子树中的数都在别的子树中。
然后,我们需要按照拓扑序倒着往里填。例如,子树 \(A\) 中 \(x\) 在最上面,填进子树 \(B\) 的时候 \(x\) 也要在最上面。如此,在任何的 \(x\) 链上,\(x\) 和 \(x\) 子树中的值都是逆序出现的,最多造成 \(1\) 的贡献。
如果树有两个重心,从重心边分成两棵树直接填充。
如果树只有一个重心,将所有分割出的子树从重心开始的 \(\text{dfs}\) 序跑出来存在 \(\text{vector}\) 里面,每次找到最大的两个 \(\text{vector}\) 进行配对。我们发现,如果我们可以把 \(x\) 放在 \(y\),也可以把 \(y\) 放在 \(x\)。这样,因为是重心,剩下的节点最多只有 \(1\) 个,如果剩下了,就和重心配对,否则重心和自己配对。
为什么重心当根就一定能匹配到只剩 \(0\) 或 \(1\) 个结点?
因为是重心当根,就没有一个分出来的子树多于 \(dfrac{n}{2}\) 个节点。应用数学归纳法,\(n=2\) 和 \(n=3\) 时,结论显然成立。然后当前点数 \(n>3\),最大子树小于 \(dfrac{n}{2}\) 的情况中,在最大的子树中找一个点,再随便找一个别的点,此时 \(n'=n-2\),且最大子树的点数依然不超过 \(\dfrac{n'}{2}\),得证。
每次找到最大的 \(\text{vector}\) 需要使用 \(\text{priority_queue}\) 维护 \(\text{vector}\) 大小和下标,每次弹出后更新大小加入。总复杂度 \(O(n\log n)\)。
int n,sz[5005],a,b,pa[5005];
vt<int>vv[5005];
inline void dfs(int x,int p){
sz[x]=1,pa[x]=p;
for(auto j:vv[x])if(j!=p){
dfs(j,x);sz[x]+=sz[j];
}
}
inline int cen(int x,int p){
for(auto j:vv[x])if(j!=p){
if(sz[j]*2>=n)return cen(j,x);
}
return x;
}
inline void dfss(int x,int p,vt<int>&v){
v.pb(x);
for(auto j:vv[x])if(j!=p){
dfss(j,x,v);
}
}
int ans[5005];
inline void solve1(int r){
vt<vt<int> >vs;
ans[r]=r;
for(auto j:vv[r]){
vt<int>v;
dfss(j,r,v);
vs.pb(v);
}
priority_queue<pii>pq;
rd(i,vs.size())pq.push({vs[i].size(),i});
while(pq.size()){
if(pq.size()==1){
int x=pq.top().second;pq.pop();
ans[vs[x].back()]=r;
ans[r]=vs[x].back();
break;
}
int x=pq.top().second;pq.pop();
int y=pq.top().second;pq.pop();
ans[vs[x].back()]=vs[y].back();
ans[vs[y].back()]=vs[x].back();
vs[x].pop_back();vs[y].pop_back();
if(vs[x].size())pq.push({vs[x].size(),x});
if(vs[y].size())pq.push({vs[y].size(),y});
}
rp(i,n)cout<<ans[i]<<" ";
cout<<endl;
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
cin>>n;
rp(i,n-1){
cin>>a>>b;
vv[a].pb(b);
vv[b].pb(a);
}
dfs(1,0);
int c1=cen(1,0),c2=c1;
bool flag=0;
if(n%2==0&&sz[c1]==n/2)c2=pa[c1],flag=1;
else if(n%2==0){
for(auto j:vv[c1])if(j!=pa[c1]&&sz[j]==n/2)c2=j,flag=1;
}
if(!flag){
solve1(c1);
return 0;
}
vt<int>v1,v2;
dfss(c1,c2,v1);
dfss(c2,c1,v2);
while(v1.size()){
ans[v1.back()]=v2.back();
ans[v2.back()]=v1.back();
v1.pop_back();v2.pop_back();
}
rp(i,n)cout<<ans[i]<<" ";
cout<<endl;
return 0;
}
//Crayan_r