bzoj1086-王室联邦

题目

给出一棵树,求一种分块方案,使得每个块的大小\(size\in [B,3B]\)。每个块还要选一个省会,省会可以在块外,但是省会到块内任何一个点路径上的所有除了省会的点都必须属于这个块。\(n\le 1000\)

分析

一次dfs即可解决。做法如下:

  • 任意选取一个点开始dfs
  • 对一个点,搜索它的所有儿子
  • 如果搜索完一个儿子后发现栈中元素大于\(B\)个,那么把栈中的元素分成一个块
  • 否则继续搜索
  • 一个点dfs结束时把它本身入栈

实现中有一个问题,对于一个儿子,它在栈中积累了不到\(B\)个点,而在下一个搜索的儿子的下层达到了\(B\)个点,那么这样两个块就会不连通,出现问题。所以我们每次设置一个bottom,意为对于这个儿子它的栈底是哪里,这样就可以保证不会弹出之前的节点,从而保证连通性。每次弹栈的时候把当前搜索的节点设为省会,就一定能够符合要求。最后搜索完之后栈中会剩下一些节点,而这些节点必定与最后一个块连通,而最后一个块的节点个数和剩下的节点个数和\(sum\in (2B,3B]\),所以可以归进同一个块内。代码实现非常简单。

这道题是树分块方法的模版题,树分块在很多树上询问问题中都有应用。

代码

#include<cstdio>
#include<cctype>
#include<algorithm>
#include<cstring>
int read() {
    int x=0,f=1;
    char c=getchar();
    for (;!isdigit(c);c=getchar()) if (c=='-') f=-1;
    for (;isdigit(c);c=getchar()) x=x*10+c-'0';
    return x*f;
}
const int maxn=1e3+1;
int n,b,gs=0;
int sta[maxn],top=0;
int id[maxn],cap[maxn];
struct edge {
    int v,nxt;
} e[maxn<<1];
int h[maxn],tot=0;
void add(int u,int v) {
    e[++tot]=(edge){v,h[u]};
    h[u]=tot;
}
void dfs(int x,int fa,int bot) {
    for (int i=h[x],v=e[i].v;i;i=e[i].nxt,v=e[i].v) if (v!=fa) {
        dfs(v,x,top);
        if (top-bot>=b) {
            ++gs;
            cap[gs]=x;
            while (top!=bot) id[sta[top--]]=gs;
        }
    }
    sta[++top]=x;
}
int main() {
    #ifndef ONLINE_JUDGE
        freopen("test.in","r",stdin);
    #endif
    n=read(),b=read();
    for (int i=1;i<n;++i) {
        int u=read(),v=read();
        add(u,v),add(v,u);
    }
    dfs(1,0,top);
    while (top) id[sta[top--]]=gs;
    printf("%d\n",gs);
    for (int i=1;i<=n;++i) printf("%d ",id[i]);
    puts("");
    for (int i=1;i<=gs;++i) printf("%d ",cap[i]);
    puts("");
    return 0;
}
posted @ 2017-04-17 20:22  permui  阅读(...)  评论(... 编辑 收藏