BZOJ 1086 王室联邦 树分块

9 篇文章 0 订阅

大家都很强, 可与之共勉 。

我们开一个栈,遍历一个节点,若该节点的几棵子树的大小>=B,那么就把他们分到一块,省会为当前节点
这样做会剩下不到B个节点,这时候就利用栈传到上一层节点就可以
最后会剩下不到B个节点,因为我们原来的块都是一定不超过2B的,于是把这B个节点放到最后一个块也不会超过3B。

这里用到了贪心的思想。

1086: [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
Sample Output

3

2 1 1 3 3 3 3 2

2 1 8
HINT

Source

/**************************************************************
    Problem: 1086
    User: Lazer2001
    Language: C++
    Result: Accepted
    Time:0 ms
    Memory:916 kb
****************************************************************/

# define By_Lazer int main ( ) {  return 0 ;  }

// # define Local

# include <cctype>
# include <cstdio>

const size_t str = 1 << 16;

char buf[str], *ss, *tt ;

inline char pick ( )  {
     return ( ss == tt ) ? ( tt = buf + fread ( ss = buf, 1, str , stdin ), *ss ++ ) : ( *ss ++ ) ;
}

inline int read ( )  {
    static char ch;
    static int x ;
    while ( !isdigit ( ch = pick ( ) ) );
    for ( x = -48 + ch ; isdigit ( ch = pick ( ) ) ; ( x *= 10 ) += ch - 48 );
    return x ;
}

const int N = 1005 ;

struct edge  {
    int to, nxt ;
    edge ( )  {  }
    edge ( int to, int nxt ) : to ( to ), nxt ( nxt ) {   }
} g [ N << 1 ];

int ne = 1, head[N] ;

inline void AddDe ( int u, int v ) {  g [ ++ ne ] = edge ( v, head[u] ) ; head[u] = ne ; g [ ++ ne ] = edge ( u, head[v] ) ; head[v] = ne ; }

int n, b ;
int place [N], cpt [N], cnt, stack [N], top ;

inline void Dfs ( int u, int fa )  {
    int cur = top ;
    for ( int i = head [u] ; i ; i = g [i].nxt )  {
        int v = g [i].to ;
        if ( v ^ fa )  {
            Dfs ( v, u ) ;
            if ( top - cur >= b )  {
                cpt [ ++ cnt ] = u ;
                while ( cur ^ top )  {
                    place [ stack [ top -- ] ] = cnt ;
                }
            }
        }
    }
    stack [ ++ top ] = u ;
}

class Main  {
public :
    Main ( )  {
        # ifdef Local
            freopen ( "in.txt", "r", stdin ) ;
            freopen ( "out.txt", "w", stdout ) ;
        # endif
        n = read ( ), b = read ( ) ;

        for ( int i = 1 ; i < n ; ++ i )  AddDe ( read ( ), read ( ) ) ;

        Dfs ( 1, 0 ) ;

        while ( top )  place [ stack [top --] ] = cnt ;

        printf ( "%d\n", cnt ) ;
        for ( int i = 1 ; i <= n ; ++ i )  printf ( "%d%c", place [i], ( n ^ i ) ? ' ' : '\n' ) ;
        for ( int i = 1 ; i <= cnt ; ++ i )  printf ( "%d%c", cpt [i], ( cnt ^ i ) ? ' ' : '\n' ) ;
    }
} Z ;

By_Lazer
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值