[POI2011] INS-Inspection

分析

看到标签里写的 dp,想了想可能是换根,但我不会,怎么办呢?

考虑什么时候会是 \(-1\)。观察样例发现,只有行动中心为 \(2\) 的时候才不是 \(-1\),而 \(2\) 恰好是树的重心,那么猜想只有重心才不是 \(-1\),接下来证明它。

如果一个点不是重心,那么说明至少存在其中一个子树 \(T'\) 大小大于 \(\frac{n}{2}\),那么剩下的子树大小之和一定小于 \(\frac{n}{2}-1\),则我们每次访问完 \(T'\) 中的一个节点后,要保证相邻两次不走重复道路,就需要访问一个 \(T'\) 外的节点,如此到最后,会发现除了 \(T'\) 中的 \(2\) 个点以外其它点都访问完了,这时但凡再去访问一个点,剩下那个点就访问不到了,因此不是重心的点都是 \(-1\)

做到这一步了,下一步就开始想对于重心应该输出什么。这时候,如果恰有子树 \(T'\) 的节点树为 \(\frac{n}{2}\),那么我们必须先访问 \(T'\) 中的一个点,否则就会最后剩下 \(2\)\(T'\) 中的点没访问,又无解了。既然需要先访问 \(T'\) 中的点,那么最后必定也会剩下 \(T'\) 中的点,我们想让最后这条路径尽量长(否则它要被计算两次),所以就选择 \(T'\) 中深度最大的点。综上,如果重心 \(c\) 存在一个子树 \(T'\) 的节点个数为 \(\frac{n}{2}\),答案为 \(2\times\displaystyle\sum_x\operatorname{dist}(c,x)-\max_x(\operatorname{dist(c,x)}\times\{x\in T'\})\),其中 \(\operatorname{dist}(i,j)\) 表示 \(i,j\) 的距离,\(\{x\in T'\}\) 表示 \(\begin{cases} 1 &\text{if } x\in T' \\ 0 &\text{if } x\not \in T' \end{cases}\)

如果不存在这样的子树 \(T'\),那么对选择没有要求,又因为 \(c\) 是重心,必然存在合法解,那么最优肯定是选择深度最大的结尾,因此此时答案为 \(2\times\displaystyle\sum_x\operatorname{dist}(c,x)-\max_x(\operatorname{dist(c,x)})\)

由于重心最多 \(2\) 个,所以复杂度 \(O(n)\)

AC Code

#include <bits/stdc++.h>
using namespace std;
const int N=1e6+10;
int maxx[N],is[N],sz[N],n,dep[N],rt,son[N],tag[N];
vector<int> G[N];
void dfs(int u,int fa)
{
	sz[u]=1;
	maxx[u]=0;
	for(auto v:G[u])
	{
		if(v==fa) continue;
		dfs(v,u);
		sz[u]+=sz[v];
		maxx[u]=max(maxx[u],sz[v]);
		if(maxx[u]==n/2&&!son[u]) son[u]=v;
	}
	maxx[u]=max(maxx[u],n-sz[u]);
	if(maxx[u]==n/2&&!son[u]) son[u]=fa;
	if(maxx[u]<=(n>>1)) is[u]=1;
}
void dfs2(int u,int fa)
{
	tag[u]=(u==son[rt])|tag[fa];
	if(fa==rt) dep[u]=1;
	for(auto v:G[u])
	{
		if(v==fa) continue;
		dep[v]=dep[u]+1;
		dfs2(v,u);
	}
}
int main()
{
	cin>>n;
	for(int i=1;i<n;i++)
	{
		int u,v;
		cin>>u>>v;
		G[u].push_back(v);
		G[v].push_back(u);
	}
	dfs(1,0);
	for(int i=1;i<=n;i++)
	{
		if(!is[i]) cout<<-1<<endl;
		else
		{
			memset(dep,0,sizeof dep);
			memset(tag,0,sizeof tag);
			rt=i;
			dfs2(i,0);
			int ans=0,maxd=0;
			for(int i=1;i<=n;i++) ans+=dep[i];
			if(!son[i])
			for(int i=1;i<=n;i++) maxd=max(maxd,dep[i]);
			else
			for(int i=1;i<=n;i++) maxd=max(maxd,dep[i]*tag[i]);
			cout<<ans*2-maxd<<endl;
		}
	}
	return 0;
}
posted @ 2024-01-13 22:23  Crazyouth  阅读(2)  评论(0编辑  收藏  举报