bzoj1086[SCOI2005]王室联邦
传送门
Description
“余”人国的国王想重新编制他的国家。他想把他的国家划分成若干个省,每个省都由他们王室联邦的一个成
员来管理。他的国家有n个城市,编号为1..n。一些城市之间有道路相连,任意两个不同的城市之间有且仅有一条
直接或间接的道路。为了防止管理太过分散,每个省至少要有B个城市,为了能有效的管理,每个省最多只有3B个
城市。每个省必须有一个省会,这个省会可以位于省内,也可以在该省外。但是该省的任意一个城市到达省会所经
过的道路上的城市(除了最后一个城市,即该省省会)都必须属于该省。一个城市可以作为多个省的省会。聪明的
你快帮帮这个国王吧!
Input
第一行包含两个数N,B(1<=N<=1000, 1 <= B <= N)。接下来N-1行,每行描述一条边,包含两个数,即这
条边连接的两个城市的编号。
Output
如果无法满足国王的要求,输出0。否则输出数K,表示你给出的划分方案中省的个数,编号为1..K。第二行输
出N个数,第I个数表示编号为I的城市属于的省的编号,第三行输出K个数,表示这K个省的省会的城市编号,如果
有多种方案,你可以输出任意一种。
Sample Input
8 2
1 2
2 3
1 8
8 7
8 6
4 6
6 5
1 2
2 3
1 8
8 7
8 6
4 6
6 5
Sample Output
3
2 1 1 3 3 3 3 2
2 1 8
2 1 1 3 3 3 3 2
2 1 8
题解
如果有一颗子树,我们把它上面的点划分到一个省以内,那么省会一定是这个子树上的点或者子树的根节点的父亲节点。那么我们可以判断出除非n<b,否则题目一定有解。因此我们考虑对其分块,用一个栈维护。从根节点向下dfs,每次向栈中压入当前访问的值。每当子树大小大于b时,就将其记录成一块,根为省会。最后一定剩下不到b个节点,我们分的每一块的大小一定大于等于b且小于2b。所以我们把剩下的节点全部丢到最后一块中,就可以求出一个解了。
代码
1 #include<iostream> 2 #include<cstdio> 3 #include<cstdlib> 4 #include<algorithm> 5 #include<cmath> 6 using namespace std; 7 int ans=0,n,cnt=1,t=0,b; 8 struct node{ 9 int to,nxt; 10 }e[10010]; 11 int head[1010],bel[1010],mas[1010],sta[2010]; 12 void add(int u,int v){ 13 e[++cnt].to=v;e[cnt].nxt=head[u];head[u]=cnt; 14 e[++cnt].to=u;e[cnt].nxt=head[v];head[v]=cnt; 15 } 16 void dfs(int x,int fa){ 17 int now=t; 18 int i; 19 for(i=head[x];i;i=e[i].nxt){ 20 if(e[i].to!=fa){ 21 dfs(e[i].to,x); 22 if(t-now>=b){ 23 mas[++ans]=x; 24 while(t!=now){ 25 bel[sta[t--]]=ans; 26 } 27 } 28 } 29 } 30 sta[++t]=x; 31 } 32 int main(){ 33 scanf("%d%d",&n,&b); 34 if(n<b){ 35 printf("0\n");return 0; 36 } 37 int i,j,x,y; 38 for(i=1;i<n;++i){ 39 scanf("%d%d",&x,&y); 40 add(x,y); 41 } 42 dfs(1,0); 43 while(t) bel[sta[t--]]=ans; 44 printf("%d\n",ans); 45 if(ans==0) return 0; 46 for(i=1;i<=n;++i){ 47 printf("%d ",bel[i]); 48 } 49 printf("\n"); 50 for(i=1;i<=ans;++i) printf("%d ",mas[i]); 51 printf("\n"); 52 return 0; 53 }