【题解】[BalticOI 2020 Day2] 村庄
值得一想的构造题(评分虚低?
先考虑第一问,求总代价最小。
对于叶子节点,它可以和它的父节点交换。
但是它有的兄弟节点是叶子,而父节点只有一个。
我们将所有兄弟节点排成一行,那么第 \(i\) 个点移动到第 \(i+1\) 个节点,父节点移动到第 \(1\) 个节点,最后一个节点移动到父节点。这正好是一个满足条件的偏序。手算一下也不难得到如果有节点移动到父节点和兄弟节点之外的节点,一定不会使答案更优。
第二问,求总代价最大。
考虑对每条边计算贡献,假设一条边两侧的子树大小分别为 \(sz_a,sz_b\) ,那么对这条边对答案的最大贡献为 \(2\times \min\{sz_a,sz_b\}\) 。
很好理解,如果两侧子树相等,则两两互换可以卡满上线。否则最多互换 \(\min\) 对,贡献为两倍 \(\min\) 。
所以我们想着构造一种方案使得每条边都达到上线。
如果存在一条边,两侧子树大小都是 \(\dfrac{n}{2}\) ,那么两两配对即可。
但是这样的边不一定存在。\(\dfrac{n}{2}\) 这个数提供了一个新的思路,我们可以考虑割掉一个点,那么重心无疑是最优的选择,对于割掉重心后每棵子树的大小都不大于 \(\dfrac{n}{2}\) 。
那么这就是一个经典问题!我们一定可以两两配对使得每一对的两个点属于不同的子树。
用堆维护当前最大的子树,每次选择最大的子树和次大的子树中个一个点配对即可。注意 \(n\) 为奇数时需要特判。
时间复杂度 \(\mathcal{O}(N\log N)\) 。
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define pre(i,a,b) for(int i=a;i>=b;i--)
#define N 100005
using namespace std;
int n,h[N],tot,u[N],v[N];long long wa,wb;
struct edge{int to,nxt;}e[N<<1];
void add(int x,int y){e[++tot].nxt=h[x];h[x]=tot;e[tot].to=y;}
vector<int>c[N];
int dfs(int x,int fa){
for(int i=h[x];i;i=e[i].nxt)if(e[i].to!=fa){
int cur=dfs(e[i].to,x);
if(cur)c[x].push_back(cur);
}
if(!c[x].size())return x;
return 0;
}
void task1(){
int cur=dfs(1,0);
if(cur)c[e[h[1]].to].push_back(1);
rep(x,1,n)if(c[x].size()){
wa+=2*c[x].size();
for(int i=0;i+1<(int)c[x].size();i++)u[c[x][i]]=c[x][i+1];
u[*c[x].rbegin()]=x;u[x]=c[x][0];
}
}
int rt,mn,sz[N];
void calc(int x,int fa){
sz[x]=1;
int cur=0;
for(int i=h[x];i;i=e[i].nxt)if(e[i].to!=fa)
calc(e[i].to,x),sz[x]+=sz[e[i].to],cur=max(cur,sz[e[i].to]),wb+=2*min(sz[e[i].to],n-sz[e[i].to]);
cur=max(cur,n-sz[x]);
if(cur<mn)mn=cur,rt=x;
}
vector<int>w[N];
void ins(int x,int fa,int col){
w[col].push_back(x);
for(int i=h[x];i;i=e[i].nxt)if(e[i].to!=fa)ins(e[i].to,x,col);
}
typedef pair<int,int> Pr;
priority_queue<Pr>q;
void task2(){
mn=n;calc(1,0);
memset(sz,0,sizeof(sz));
for(int i=h[rt];i;i=e[i].nxt)ins(e[i].to,rt,e[i].to),q.push(make_pair(w[e[i].to].size(),e[i].to));
if(0==(n&1))q.push(make_pair(1,rt)),w[rt].push_back(rt);
while(!q.empty()){
int x=q.top().second,cx=q.top().first;q.pop();
int y=q.top().second,cy=q.top().first;q.pop();
//cout<<"ss "<<x<<" "<<y<<" "<<cx<<" "<<cy<<endl;
v[w[x][cx-1]]=w[y][cy-1];
v[w[y][cy-1]]=w[x][cx-1];
if(cx>1)q.push(make_pair(cx-1,x));
if(cy>1)q.push(make_pair(cy-1,y));
}
if(n&1){
int x,y;
if(rt==1)x=2,y=v[2];
else x=1,y=v[1];
v[x]=rt;v[rt]=y;v[y]=x;
}
}
int main(){
scanf("%d",&n);
rep(i,1,n-1){
int x,y;scanf("%d%d",&x,&y);
add(x,y);add(y,x);
}
task1();task2();
printf("%lld %lld\n",wa,wb);
rep(i,1,n)printf("%d ",u[i]);putchar('\n');
rep(i,1,n)printf("%d ",v[i]);putchar('\n');
return 0;
}