洛谷传送门
题目描述
“余”人国的国王想重新编制他的国家。他想把他的国家划分成若干个省,每个省都由他们王室联邦的一个成员来管理。
他的国家有 n n 个城市,编号为。一些城市之间有道路相连,任意两个不同的城市之间有且仅有一条直接或间接的道路。为了防止管理太过分散,每个省至少要有 B B 个城市,为了能有效的管理,每个省最多只有个城市。
每个省必须有一个省会,这个省会可以位于省内,也可以在该省外。但是该省的任意一个城市到达省会所经过的道路上的城市(除了最后一个城市,即该省省会)都必须属于该省。
一个城市可以作为多个省的省会。
聪明的你快帮帮这个国王吧!
输入输出格式
输入格式:
第一行包含两个数
N,B(1<=N<=1000,1<=B<=N)
N
,
B
(
1
<=
N
<=
1000
,
1
<=
B
<=
N
)
。接下来
N-1
N
-
1
行,每行描述一条边,包含两个数,即这条边连接的两个城市的编号。
输出格式:
如果无法满足国王的要求,输出
0
0
。否则第一行输出数,表示你给出的划分方案中省的个数,编号为
1..K
1..
K
。第二行输出
N
N
个数,第个数表示编号为
I
I
的城市属于的省的编号,第三行输出个数,表示这
K
K
个省的省会的城市编号,如果有多种方案,你可以输出任意一种。
输入输出样例
输入样例#1:
8 2
1 2
2 3
1 8
8 7
8 6
4 6
6 5
输出样例#1:
3
2 1 1 3 3 3 3 2
2 1 8
解题分析
这道题没什么难的, 但引申出来的树分块却非常重要。很明显此题要求在树上分块, 保证块大小、块连通性。 而这些性质正好用在树上莫队上, 因为相邻的块可以保证莫队复杂度不至于爆炸, 而均匀的块大小也正好适合莫队, 但运用dfs序或树深度分块都无法做到这一点。
考虑如何将点分在一起。 显然一开始的块越小越好, 所以我们从根节点一遍dfs下去时用栈保存所有未分至一个省的点, 每当栈中未分至一个省的点超过时就将其分为一个省,因此可以保证每个省管理不超过 2B 2 B 个点。 所以最后回到根节点时剩下的未分的点会少于 B B 个点,所以最后一个点不会超过。
其余细节见代码。
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cctype>
#define R register
#define gc getchar()
#define W while
#define IN inline
#define MX 10005
template <class T>
IN void in(T &x)
{
x = 0; R char c = gc;
W (!isdigit(c)) c = gc;
W (isdigit(c))
x = (x << 1) + (x << 3) + c - 48, c = gc;
}
struct Edge
{
int to, nex;
}edge[MX];
int head[MX], cnt, dot, st[MX], top, lim, col, tm[MX], cap[MX];
IN void addedge(const int &from, const int &to)
{
edge[++cnt] = {to, head[from]};
head[from] = cnt;
}
void DFS(const int &now, const int &fa)
{
int bot = top;
for (R int i = head[now]; i; i = edge[i].nex)
{
if(edge[i].to == fa) continue;
DFS(edge[i].to, now);
if(top - bot >= lim)
{
cap[++col] = now;
W (top > bot) tm[st[top--]] = col;
}
}
st[++top] = now;
}
int main(void)
{
int a, b;
in(dot), in(lim);
for (R int i = 1; i < dot; ++i)
in(a), in(b), addedge(a, b), addedge(b, a);
DFS(1, 0);
W (top) tm[st[top--]] = col;
printf("%d\n", col);
for (R int i = 1; i <= dot; ++i) printf("%d ", tm[i]);
putchar(10);
for (R int i = 1; i <= col; ++i) printf("%d ", cap[i]);
}