题目描述
将一棵n个点的树分为若干“块”,每个块满足:大小在B到3B之间,并且这个“块”添加某个点后连通。求方案。
输入
第一行包含两个数N,B(1<=N<=1000, 1 <= B <= N)。接下来N-1行,每行描述一条边,包含两个数,即这条边连接的两个城市的编号。
输出
如果无法满足国王的要求,输出0。否则输出数K,表示你给出的划分方案中省的个数,编号为1..K。第二行输出N个数,第I个数表示编号为I的城市属于的省的编号,第三行输出K个数,表示这K个省的省会的城市编号,如果有多种方案,你可以输出任意一种。
样例输入
8 2
1 2
2 3
1 8
8 7
8 6
4 6
6 5
样例输出
3
2 1 1 3 3 3 3 2
2 1 8
题解
树分块
给树分块的方法:
对原树进行DFS,维护一个栈,当DFS到每个点时记录栈顶位置。
当处理完某个子节点时,如果当前栈顶位置与原来位置相差大于等于B,则将它们之间的点分为一块。最后把这个点压入栈中。
最后DFS完整棵树后,栈中剩余的点放到最后一块中。
证明可以参考 VFK’s Blog
时间复杂度$O(n)$
#include <cstdio> #include <cstring> #include <algorithm> #define N 1010 using namespace std; int b , head[N] , to[N << 1] , next[N << 1] , cnt , sta[N] , top , bl[N] , v[N] , num; void add(int x , int y) { to[++cnt] = y , next[cnt] = head[x] , head[x] = cnt; } void dfs(int x , int fa) { int i , now = top; for(i = head[x] ; i ; i = next[i]) { if(to[i] != fa) { dfs(to[i] , x); if(top - now >= b) { v[++num] = x; while(top != now) bl[sta[top -- ]] = num; } } } sta[++top] = x; } int main() { int n , i , x , y; scanf("%d%d" , &n , &b); for(i = 1 ; i < n ; i ++ ) scanf("%d%d" , &x , &y) , add(x , y) , add(y , x); dfs(1 , 0); while(top) bl[sta[top -- ]] = num; printf("%d " , num); for(i = 1 ; i <= n ; i ++ ) printf("%d " , bl[i]); printf(" "); for(i = 1 ; i <= num ; i ++ ) printf("%d " , v[i]); printf(" "); return 0; }