1086: [SCOI2005]王室联邦
Time Limit: 10 Sec
Memory Limit: 162 MBSec
Special Judge
Submit: 599
Solved: 309
[ Submit][ Status]
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
Sample Output
3
2 1 1 3 3 3 3 2
2 1 8
块状树。
引用vfk的题解:
1086:王室联邦:这题真心有意思。首先以任意节点为根DFS,DFS的任务是解决以当前节点为根的子树(不包括当前节点)中的节点的归属,并汇报不知道去向何方者。具体做法是:对于每个正在处理的节点v,定义一个等待序列,扫一遍它的孩子。在扫的时候,假设当前for循环正在处理的是孩子u,DFS(u),然后把传回来的等待序列加到v的等待序列中。如果v的等待序列节点数超过了B,那么就让等待队列中的节点组成一个省,省会是v,但v不划入那个省中。最后,for循环结束,把v加入到等待序列中,return。在主函数中接收DFS的返回值,若等待序列为空,皆大欢喜;但事实上,等待序列不可能为空。那么等待序列中的节点数一定不超过B + 1,怎么办?答案就是放在上一个被划分出的省中去,那么上一个被划分出的省一定<=2B - 1,而现在最后无家可归的节点数一定<= B + 1,所以放在上一个被划分出的省中去节点数一定不超过3B!而且最后被划分出来的省一定和最后无家可归的节点中的某一个相邻!那么考虑什么时候会出现无解呢?答案就是我们找不到“上一个被划分出的省”。
我的做法是用一个栈s,dfs(x)的开始把栈底赋值为栈顶,保证从现在开始不会取到前面子树的点(如果取到的话可能不合
题意),每次处理完一个儿子,判断处理完x,判断当前栈中的剩余是否>=B,如果>=B,那么就把他们都取出来。
因为每次一超过B就会把栈中的点全部取出来,所以一个省的城市<=2B-2,回到主程序时栈中的点一定<=B,加起来<=3B-2。
(vfk这里是不是写错了??)
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cstdlib>
using namespace std;
int h[1005],top=0,tot=0,n,b,s[10005],be[1005],root[1005],cnt=0;
struct edge
{
int y,ne;
}e[5005];
void Addedge(int x,int y)
{
tot++;
e[tot].y=y;
e[tot].ne=h[x];
h[x]=tot;
}
void dfs(int x,int fa)
{
int bottom=top;
for (int i=h[x];i;i=e[i].ne)
if (e[i].y!=fa)
{
int y=e[i].y;
dfs(y,x);
if (top-bottom>=b)
{
root[++cnt]=x;
while (top!=bottom)
be[s[top--]]=cnt;
}
}
s[++top]=x;
}
int main()
{
scanf("%d%d",&n,&b);
for (int i=1;i<n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
Addedge(x,y);
Addedge(y,x);
}
dfs(1,0);
while (top)
be[s[top--]]=cnt;
cout<<cnt<<endl;
for (int i=1;i<=n;i++)
cout<<be[i]<<" ";
cout<<endl;
for (int i=1;i<=cnt;i++)
cout<<root[i]<<" ";
cout<<endl;
return 0;
}
感悟:
学习树上莫队的必备技能~