【块状树】BZOJ 1086: [SCOI2005]王室联邦

1086: [SCOI2005]王室联邦

Time Limit: 10 Sec  Memory Limit: 162 MBSec  Special Judge
Submit: 826  Solved: 471
[Submit][Status][Discuss]

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

  很容易想到每次搜子树
  不过好像每次搜索出的残余又要和回溯上去的子树凑
  会非常麻烦
  然后只能看题解
  copy一下
  因此我们在每次进入递归时维护一个栈底,对于当前子树来说这个栈底就是整个栈的底,栈底以下的元素不能修改或弹栈
  这样当一棵子树深搜过后由于子树内未分块节点不超过b,之前搜过的未分块节点数也不超过b,因此每块不超过2b
  那么题目为什么给了3b呢?
  深搜结束后可能会剩余一些节点,这些节点的数量不超过b,而且一定与当前分出的最后一块连通
  因此我们将剩余节点分到最后一块中,可以保证最后一块的大小不超过3b
  
 1 #include<cstdio>
 2 #include<cstring>
 3 #include<cmath>
 4 #include<algorithm>
 5 
 6 #define maxn 1001
 7 
 8 using namespace std;
 9 
10 inline int in()
11 {
12     int x=0;char ch=getchar();
13     while(ch<'0'||ch>'9')ch=getchar();
14     while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
15     return x;
16 }
17 
18 int n,l,last[maxn],tot=0,sta[maxn],top=0,father[maxn],root[maxn],cnt=0;
19 
20 struct ed{
21     int to,last;
22 }edge[maxn*2];
23 
24 void add(int u,int v)
25 {
26     edge[++tot].to=v,edge[tot].last=last[u],last[u]=tot;
27     edge[++tot].to=u,edge[tot].last=last[v],last[v]=tot;
28 }
29 
30 void dfs(int poi,int Last)
31 {
32     int lim=top;
33     for(int i=last[poi];i;i=edge[i].last)if(edge[i].to!=Last){
34         dfs(edge[i].to,poi);
35         if(top-lim>=l)
36         {
37             root[++cnt]=poi;
38             while(top!=lim)father[sta[top--]]=cnt;
39         }
40     }
41     sta[++top]=poi;
42 }
43 
44 int main()
45 {
46     int u,v;
47     n=in();l=in();
48     for(int i=1;i<n;i++)
49         u=in(),v=in(),add(u,v);
50     dfs(1,0);
51     while(top)father[sta[top--]]=cnt;
52     printf("%d\n",cnt);
53     for(int i=1;i<=n;i++)printf("%d ",father[i]);
54     printf("\n");
55     for(int i=1;i<=cnt;i++)printf("%d ",root[i]);
56     return 0;
57 }
View Code

 

  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了小程序应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值