题解:luogu P6697([BalticOI 2020] 村庄 (Day2))

1. Description

给定一棵有 \(n\) 个节点的树,点从 \(1\sim n\) 编号,现在请求出两个 \(v_i\) 序列,满足:

  1. \(v_i\in[1,n]\)
  2. \(i\ne v_i\)
  3. \(\forall i\ne j,v_i\ne v_j\)

且分别使 \(\operatorname{dist}(i,v_i)\) 最大或最小,其中 \(\operatorname{dist}(u,v)\) 表示 \(u,v\) 在树上的距离。

2. Solution

首先考虑最小的情况。
首先第一直觉是,我们只会在一个节点 \(u\) 的父亲,儿子或者兄弟之间交换。
换句话说,我们对于每一个 \(u\),安排 \(u\)\(v\in son_u\) 有哪些在此时决定自己的目的地,最后进行轮换即可。
但这并不准确,例如在样例二中,最优方案如下:
graph1777980543414
我们发现 \(6\) 号点的目的地实际上是 \(1\),而不是他的父亲,这启示我们,在 \(u\) 的子树中,除了 \(u\) 本身可以向上匹配,\(u\) 的儿子也可以向上匹配。
很显然,最多只有一个 \(u\) 的儿子会向上匹配,并且存在一个 \(u\) 的儿子向上匹配,当且仅当 \(u\) 本身也向上匹配。
原因很简单,如果存在两个 \(u\) 的儿子 \(x,y\) 均向上匹配,则 \(x\rightarrow u,y\rightarrow u\) 这两条边分别走两次,\(u\rightarrow fa_u\) 这条边则需要走四次,因为 \(x,y\) 都需要走出 \(u\) 这棵子树,而子树外也一定有两个点分别走到 \(x,y\),而如果我们让 \(x,y\) 相互匹配,则不需要经过 \(u\rightarrow fa_u\) 这条边,一定更优。
如果 \(u\) 匹配了子树中的 \(v\),而 \(x\) 向上匹配,则我们让 \(x\) 匹配 \(v\),让 \(u\) 向外匹配一定不会让答案更劣。
由此,我们定义 dp 状态 \(f_{u,0/1/2}\) 表示 \(u\) 这棵子树中,存在 \(0/1/2\) 个点向子树外匹配的最小代价。
考虑转移:
对于 \(f_{u,0}\) 而言,\(u\) 需要参与子树内的匹配,则 \(u\) 的所有儿子 \(v\) 中,至少有 \(1\) 个点没有在自己的子树中参与匹配。
对于 \(f_{u,1}\) 而言,\(u\) 不参与子树内的匹配,则 \(u\) 的所有儿子 \(v\) 中要么没有点需要匹配,要么至少有两个点没有在自己的子树中参与匹配。
对于 \(f_{u,2}\) 而言,\(u\)\(u\) 的一个儿子不参与子树内的匹配,则 \(u\) 的所有儿子 \(v\) 中要么只有一个点需要匹配,要么至少有三个点没有在自己的子树中参与匹配。
由此,我们定义一个 \(g_{u,0/1/2/3}\) 表示 \(u\) 的所有儿子有 \(0/1/2/\ge 3\) 个点需要向外匹配的最小代价和,最后只需 \(f_{u,0}\leftarrow \min(g_{u,1},g_{u,2},g_{u,3}),f_{u,1}\leftarrow \min(g_{u,0},g_{u,2},g_{u,3}),f_{u,2}\leftarrow \min(g_{u,1},g_{u,3})\)
最后的最小值就是 \(f_{1,0}\)
这里有一点需要注意的,无论 \(f_{v,1}\) 还是 \(f_{v,2}\),向外匹配时 \(v\rightarrow u\) 这条边都只会走两次。
那我们该怎么求出方案呢?
首先,假定我们现在要求出 \(f_{u,opt}\) 的方案,通过记录转移路径求出 \(g_{u,0/1/2/3}\) 分别用了 \(f_v\) 的哪个状态转移而来,然后根据 \(f_{u,opt}\) 的值,求出 \(opt^\prime\) 表示 \(f_{u,opt}=g_{u,opt^\prime}\),接着递归下去解决 \(v\) 的子树内的方案。
对于在 \(u\) 处参与交换的点,我们首先记录下所有参与交换的点,然后按照 dfs 序排序,然后依次匹配即可。
然后考虑最大的情况。
我们可以得到理论上界为 \(2\sum_{i=1}^n \min(siz_i,n-siz_i)\),即断开每一条边之后,尽可能将两边的子树相互匹配。
其实这个上界很好得到,我们选取重心为根,求出 dfs 序,将所有 dfs 序相差 \(\lfloor \frac{n}{2}\rfloor\) 的点合为一组,接着组内按照 dfs 序排序之后依次匹配即可。
取到上界的原因显然,这里不过多赘述。

3. Code

/*by ChenMuJiu*/
/*略去缺省源与快读快写*/
const int N=1e5+5,inf=0x3f3f3f3f;
int n;
vector<int>e[N];
namespace Subtask1{
int ans,cnt_dfn;
int ansp[N],f[N][3],g[N][4],tmp[4],dfn[N],h[N];
pii path[N][4];
int pre[N];
bool cmp(int x,int y){
	return dfn[x]<dfn[y];
}
void dfs(int u,int fa){
	dfn[u]=++cnt_dfn;
	
	g[u][0]=0;
	for(int v:e[u]){
		if(v==fa)continue;
		dfs(v,u);
		for(int i=0;i<4;i++)
			for(int j=0;j<3;j++)
				tomin(tmp[min(3,i+j)],g[u][i]+f[v][j]+(j>0)*2);
		for(int i=0;i<4;i++)
			g[u][i]=tmp[i],tmp[i]=inf;
	}
	f[u][0]=min({g[u][1],g[u][2],g[u][3]});
	f[u][1]=min({g[u][0],g[u][2],g[u][3]});
	f[u][2]=min({g[u][1],g[u][3]});
}
void getpath(int now,int opt,vector<pii> &p){
	while(~now){
		p.push_back({now,path[now][opt].second});
		opt=path[now][opt].first;
		now=pre[now];
	}
}
void redfs(int u,int fa,int opt){
	g[u][0]=0,g[u][1]=inf,g[u][2]=inf,g[u][3]=inf;
	
	int las=-1;
	for(int v:e[u]){
		if(v==fa)continue;
		pre[v]=las,las=v;
		for(int i=0,to;i<4;i++)
			for(int j=0;j<3;j++){
				to=min(3,i+j);
				if(tmp[to]>g[u][i]+f[v][j]+(j>0)*2){
					tmp[to]=g[u][i]+f[v][j]+(j>0)*2;
					path[v][to]={i,j};
				}
			}
		for(int i=0;i<4;i++)
			g[u][i]=tmp[i],tmp[i]=inf;
	}
	vector<pii> p[4];
	getpath(las,0,p[0]);
	getpath(las,1,p[1]);
	getpath(las,2,p[2]);
	getpath(las,3,p[3]);
	vector<int>idx;
	if(opt==0){
		int _opt;
		if(f[u][0]==g[u][1])_opt=1;
		else if(f[u][0]==g[u][2])_opt=2;
		else if(f[u][0]==g[u][3])_opt=3;
		idx.push_back(u);
		for(auto tmp:p[_opt]){
			redfs(tmp.first,u,tmp.second);
			if(tmp.second==1)idx.push_back(tmp.first);
			else if(tmp.second==2){
				idx.push_back(tmp.first);
				idx.push_back(h[tmp.first]);
			}
		}
	}else if(opt==1){
		int _opt;
		if(f[u][1]==g[u][0])_opt=0;
		else if(f[u][1]==g[u][2])_opt=2;
		else if(f[u][1]==g[u][3])_opt=3;
		for(auto tmp:p[_opt]){
			redfs(tmp.first,u,tmp.second);
			if(tmp.second==1)idx.push_back(tmp.first);
			else if(tmp.second==2){
				idx.push_back(tmp.first);
				idx.push_back(h[tmp.first]);
			}
		}
	}else if(opt==2){
		int _opt;
		if(f[u][2]==g[u][1])_opt=1;
		else if(f[u][2]==g[u][3])_opt=3;
		for(auto tmp:p[_opt]){
			redfs(tmp.first,u,tmp.second);
			if(tmp.second==1)idx.push_back(tmp.first);
			else if(tmp.second==2){
				idx.push_back(tmp.first);
				idx.push_back(h[tmp.first]);
			}
		}
		h[u]=idx.back();
		idx.pop_back();
	}
	int siz=idx.size();
	if(siz){
		sort(idx.begin(),idx.end(),cmp);
		for(int i=0;i<siz-1;i++)
			ansp[idx[i]]=idx[i+1];
		ansp[idx[siz-1]]=idx[0];	
	}
}
void solve(){
	memset(f,0x3f,sizeof(f));
	memset(g,0x3f,sizeof(g));
	memset(tmp,0x3f,sizeof(tmp));
	dfs(1,0);
	
	ans=f[1][0];
	redfs(1,0,0);
}
}
namespace Subtask2{
int rt,cnt_dfn,d;
ll ans;
int ansp[N],siz[N],f[N],dfn[N];
vector<int>id[N];
bool cmp(int x,int y){
	return dfn[x]<dfn[y];
}
void dfs(int u,int fa){
	siz[u]=1;
	for(int v:e[u]){
		if(v==fa)continue;
		dfs(v,u);
		tomax(f[u],siz[v]);
		siz[u]+=siz[v];
	}
	ans+=min(siz[u],n-siz[u]);
	tomax(f[u],n-siz[u]);
	if(rt==-1||f[rt]>f[u])
		rt=u;
}
void redfs(int u,int fa){
	dfn[u]=++cnt_dfn;
	for(int v:e[u]){
		if(v==fa)continue;
		redfs(v,u);
	}
}
void solve(){
	rt=-1;
	dfs(1,0);
	ans<<=1;
	redfs(rt,0);
	d=cnt_dfn>>1;
	for(int i=1;i<=n;i++)
		id[dfn[i]%d].push_back(i);
	for(int t=0;t<d;t++){
		vector<int>& idx=id[t];
		
		int siz=idx.size();
		if(siz){
			sort(idx.begin(),idx.end(),cmp);
			for(int i=0;i<siz-1;i++)
				ansp[idx[i]]=idx[i+1];
			ansp[idx[siz-1]]=idx[0];	
		}
	}
}
}
signed main(){
	//freopen("village.in","r",stdin);
	//freopen("village.out","w",stdout);
	read(n);
	for(int i=2,u,v;i<=n;i++){
		read(u),read(v);
		e[u].push_back(v);
		e[v].push_back(u);
	} 
	Subtask1::solve();
	Subtask2::solve();
	write(Subtask1::ans),Spa,write(Subtask2::ans),Nxt;
	for(int i=1;i<=n;i++)
		write(Subtask1::ansp[i]),Spa;
	Nxt;
	for(int i=1;i<=n;i++)
		write(Subtask2::ansp[i]),Spa;
	Nxt;
}
posted @ 2026-05-14 14:06  陈牧九  阅读(3)  评论(0)    收藏  举报