P2325 [SCOI2005]王室联邦
题目描述
“余”人国的国王想重新编制他的国家。他想把他的国家划分成若干个省,每个省都由他们王室联邦的一个成员来管理。
他的国家有n个城市,编号为1..n。一些城市之间有道路相连,任意两个不同的城市之间有且仅有一条直接或间接的道路。为了防止管理太过分散,每个省至少要有B个城市,为了能有效的管理,每个省最多只有3B个城市。
每个省必须有一个省会,这个省会可以位于省内,也可以在该省外。但是该省的任意一个城市到达省会所经过的道路上的城市(除了最后一个城市,即该省省会)都必须属于该省。
一个城市可以作为多个省的省会。
聪明的你快帮帮这个国王吧!
输入输出格式
输入格式:
第一行包含两个数N,B(1<=N<=1000, 1 <= B <= N)。接下来N-1行,每行描述一条边,包含两个数,即这条边连接的两个城市的编号。
输出格式:
如果无法满足国王的要求,输出0。否则第一行输出数K,表示你给出的划分方案中省的个数,编号为1..K。第二行输出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
说明
感谢@FlierKing提供spj
题解
dfs过程中每退出一个点,将这个点加入栈中。对于某个节点,如果他的一些子树能够构成>=B的块,则将他们作为一个快。剩余的一部分加入最后一个块中。可以证明,每次形成的块大小一定大于等于B小于2B,而剩余的一部分大小小于等于B,最后一个快不会超过3B
这种分块方法,块内不一定联通
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cstdlib>
#include <iostream>
#include <bitset>
#include <cmath>
void swap(int &a, int &b){int tmp = a;a = b, b = tmp;}
int max(int a, int b){return a > b ? a : b;}
int min(int a, int b){return a < b ? a : b;}
void read(int &x)
{
x = 0;char ch = getchar(), c = ch;
while(ch < '0' || ch > '9') c = ch, ch = getchar();
while(ch <= '9' && ch >= '0') x = x * 10 + ch - '0', ch = getchar();
if(c == '-') x = -x;
}
const int INF = 0x3f3f3f3f;
const int MAXN = 1000 + 10;
struct Edge
{
int u, v, nxt;
Edge(int _u, int _v, int _nxt){u = _u, v = _v, nxt = _nxt;}
Edge(){}
}edge[MAXN << 1];
int head[MAXN], cnt, n;
void insert(int a, int b)
{
edge[++ cnt] = Edge(a, b, head[a]), head[a] = cnt;
edge[++ cnt] = Edge(b, a, head[b]), head[b] = cnt;
}
int belong[MAXN], stack[MAXN], top, cap[MAXN], B, num;
void dfs(int x, int pre)
{
int now = top;
for(int pos = head[x];pos;pos = edge[pos].nxt)
{
int v = edge[pos].v;
if(pre == v) continue;
dfs(v, x);
if(top - now >= B)
for(++ num, cap[num] = x;now < top;-- top)
belong[stack[top]] = num;
}
stack[++ top] = x;
}
int main()
{
read(n), read(B);
for(int i = 1;i < n;++ i)
{
int tmp1, tmp2;
read(tmp1), read(tmp2);
insert(tmp1, tmp2);
}
dfs(1, 0);
for(int i = 1;i <= n;++ i)
if(belong[i] == 0)
belong[i] = num;
printf("%d\n", num);
for(int i = 1;i <= n;++ i)
printf("%d ", belong[i]);
printf("\n");
for(int i = 1;i <= num;++ i)
printf("%d ", cap[i]);
return 0;
}