【P2325 [SCOI2005]王室联邦】题解

题目链接

题目

“余”人国的国王想重新编制他的国家。他想把他的国家划分成若干个省,每个省都由他们王室联邦的一个成员来管理。

他的国家有 \(N\) 个城市,编号为 \(1\ldots N\)

一些城市之间有道路相连,任意两个不同的城市之间有且仅有一条直接或间接的道路。

为了防止管理太过分散,每个省至少要有 \(B\) 个城市。

为了能有效的管理,每个省最多只有 \(3\times B\) 个城市。

每个省必须有一个省会,这个省会可以位于省内,也可以在该省外。

但是该省的任意一个城市到达省会所经过的道路上的城市(除了最后一个城市,即该省省会)都必须属于该省。

一个城市可以作为多个省的省会。

聪明的你快帮帮这个国王吧!

思路

一道非常好的树上构造题。

首先我们先dfs,对于当前点 \(x\),当其叶子节点(假设为 \(y\))返回以 \(y\) 为根的子树内未分配的节点,我们把它加入到一个集合(假设为 \(S\))。

\(S\) 的大小大于 \(B\) 时,我们就可以把 \(S\) 划分到一个省内。

当对 \(x\) 的子树dfs完后,我们把 \(x\) 加入到集合 \(S\) 中,并返回。

这里我们可以证明每次返回的集合 \(S\) 的大小严格小于等于 \(B\)

  • 在未加入 \(x\) 点之前,如果 \(S\) 集合大于 \(B\),按照我们的步骤我们会把它们划分成一个省。所以划分完后,\(S\) 的大小最大为 \(B-1\),加入 \(x\) 后最大为 \(B\)。所以 \(|S|\leqslant B\)

因此我们也可以证明每次划分出来的省的大小都小于等于 \(2\times B-1\)

  • 由于划分前 \(S\) 的大小最大为 \(B-1\)(前面已证),子树返回的大小最大为 \(B\),故省的大小最大为 \(2\times B-1\)

dfs后如果集合 \(S\) 非空,我们则把它放入最后一个省里。我们可以证明最后一个省严格小于等于 \(3\times B-1\)

  • 由于最后的集合 \(S\) 未加入之前省的大小最大为 \(2\times B-1\),集合 \(S\) 的大小最大为 \(B\),故加入后这个省的大小最大为 \(3\times B-1\)

时间复杂度:\(O(n)\)

要注意的是dfs后要先判断是否存在省再加入。

集合 \(S\) 可以用栈来实现。

Code

// Problem: P2325 [SCOI2005]王室联邦
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P2325
// Memory Limit: 125 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include<bits/stdc++.h>
using namespace std;
#define int long long
inline int read(){int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;
ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+
(x<<3)+(ch^48);ch=getchar();}return x*f;}
//#define M
//#define mo
#define N 1010
struct node
{
	int x, y, k, n; 
}d[N*2]; 
int n, m, i, j, k; 
int h[N], rot[N], zhan[N], shu[N]; 
int num, top, u, v; 

void cun(int x, int y)
{
	d[++k].x=x; d[k].y=y; 
	d[k].n=h[x]; h[x]=k; 
}

void dfs(int x, int fa)
{
	int now=top; 
	for(int g=h[x]; g; g=d[g].n)
	{
		int y=d[g].y; 
		if(y==fa) continue ;
		dfs(y, x); 
		if(top-now>=m)
		{
			rot[++num]=x; 
			while(top>now) shu[zhan[top--]]=num; 
		}
	}
	zhan[++top]=x; 
}

signed main()
{
//	freopen("tiaoshi.in", "r", stdin); 
//	freopen("tiaoshi.out", "w", stdout); 
	n=read(); m=read(); 
	for(i=1; i<n; ++i)
	{
		u=read(); v=read(); 
		cun(u, v); cun(v, u); 
	}
	dfs(1, 0); 
	if(num==0) rot[++num]=1; 
	while(top) shu[zhan[top--]]=num; 
	printf("%lld\n", num); 
	for(i=1; i<=n; ++i) printf("%lld ", shu[i]); 
	printf("\n"); 
	for(i=1; i<=num; ++i) printf("%lld ", rot[i]); 
	return 0; 
}

posted @ 2021-11-16 22:16  zhangtingxi  阅读(27)  评论(0编辑  收藏  举报