[Luogu P2325] [SCOI2005]王室联邦

10 篇文章 0 订阅
洛谷传送门

题目描述

“余”人国的国王想重新编制他的国家。他想把他的国家划分成若干个省,每个省都由他们王室联邦的一个成员来管理。

他的国家有 n n 个城市,编号为1..n。一些城市之间有道路相连,任意两个不同的城市之间有且仅有一条直接或间接的道路。为了防止管理太过分散,每个省至少要有 B B 个城市,为了能有效的管理,每个省最多只有3B个城市。

每个省必须有一个省会,这个省会可以位于省内,也可以在该省外。但是该省的任意一个城市到达省会所经过的道路上的城市(除了最后一个城市,即该省省会)都必须属于该省。

一个城市可以作为多个省的省会。

聪明的你快帮帮这个国王吧!

输入输出格式
输入格式:
第一行包含两个数 NB1<=N<=1000,1<=B<=N N , B ( 1 <= N <= 1000 , 1 <= B <= N ) 。接下来 N1 N - 1 行,每行描述一条边,包含两个数,即这条边连接的两个城市的编号。

输出格式:
如果无法满足国王的要求,输出 0 0 。否则第一行输出数K,表示你给出的划分方案中省的个数,编号为 1..K 1.. K 。第二行输出 N N 个数,第I个数表示编号为 I I 的城市属于的省的编号,第三行输出K个数,表示这 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 

解题分析

这道题没什么难的, 但引申出来的树分块却非常重要。很明显此题要求在树上分块, 保证块大小、块连通性。 而这些性质正好用在树上莫队上, 因为相邻的块可以保证莫队复杂度不至于爆炸, 而均匀的块大小也正好适合莫队, 但运用dfs序或树深度分块都无法做到这一点。

考虑如何将点分在一起。 显然一开始的块越小越好, 所以我们从根节点一遍dfs下去时用栈保存所有未分至一个省的点, 每当栈中未分至一个省的点超过B时就将其分为一个省,因此可以保证每个省管理不超过 2B 2 B 个点。 所以最后回到根节点时剩下的未分的点会少于 B B 个点,所以最后一个点不会超过3B

其余细节见代码。

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cctype>
#define R register
#define gc getchar()
#define W while
#define IN inline
#define MX 10005
template <class T>
IN void in(T &x)
{
    x = 0; R char c = gc;
    W (!isdigit(c)) c = gc;
    W (isdigit(c))
    x = (x << 1) + (x << 3) + c - 48, c = gc;
}
struct Edge
{
    int to, nex;
}edge[MX];
int head[MX], cnt, dot, st[MX], top, lim, col, tm[MX], cap[MX];
IN void addedge(const int &from, const int &to)
{
    edge[++cnt] = {to, head[from]};
    head[from] = cnt;
}
void DFS(const int &now, const int &fa)
{
    int bot = top;
    for (R int i = head[now]; i; i = edge[i].nex)
    {
        if(edge[i].to == fa) continue;
        DFS(edge[i].to, now);
        if(top - bot >= lim)
        {
            cap[++col] = now;
            W (top > bot) tm[st[top--]] = col;
        }
    }
    st[++top] = now;
}
int main(void)
{
    int a, b;
    in(dot), in(lim);
    for (R int i = 1; i < dot; ++i)
    in(a), in(b), addedge(a, b), addedge(b, a);
    DFS(1, 0);
    W (top) tm[st[top--]] = col;
    printf("%d\n", col);
    for (R int i = 1; i <= dot; ++i) printf("%d ", tm[i]);
    putchar(10);
    for (R int i = 1; i <= col; ++i) printf("%d ", cap[i]);
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值