[scoi2005]王室联邦 解题报告

拿来学了一下树分块。
树分块的要求是把树分成 NB 块,每一块的每个节点到这个块的lca的之间的节点数不超过 3B .
(好像在很久以前听谁讲过。。)做法是按dfs序出栈或bfs倒序考虑,把当前这个子树的剩余块加到它的父亲上,如果它的父亲上的块已经 b 就把这个块取出来。这样的话出来的就是若干大小在[b,2b)的块加上一个在[0,b)的块,注意到按dfs或bfs序考虑时,倒数第二个块与最后一个块一定是联通的,所以可以把它们俩并起来,这样大小就在[0,3b)了。

#include<cstdio>
#include<iostream>
using namespace std;
#include<algorithm>
#include<vector>
const int N=1000+5,B=1000+5;
int next[N<<1],succ[N<<1],ptr[N],etot=1;
void addedge(int from,int to){
    next[etot]=ptr[from],ptr[from]=etot,succ[etot++]=to;
}
int capital[N],bnum[N],btot=1;
int size[N];
int fa[N];
vector<int> son[N];
int treeq[N],blockq[N];
int main(){
    freopen("bzoj_1086.in","r",stdin);
    int n,b;
    scanf("%d%d",&n,&b);
    int u,v;
    for(int i=n;--i;){
        scanf("%d%d",&u,&v);
        addedge(u,v),addedge(v,u);
    }
    treeq[0]=1;
    for(int h=0,t=1;h!=t;++h)
        for(int i=ptr[treeq[h]];i;i=next[i])
            if(succ[i]!=fa[treeq[h]]){
                fa[succ[i]]=treeq[h];
                treeq[t++]=succ[i];
            }
    for(int h=n;h--;){
        size[fa[treeq[h]]]+=size[treeq[h]]+1;
        son[fa[treeq[h]]].push_back(treeq[h]);
        if(size[fa[treeq[h]]]>=b){
            blockq[0]=fa[treeq[h]];
            for(int h=0,t=1;h!=t;++h)
                for(int i=son[blockq[h]].size();i--;){
                    bnum[son[blockq[h]][i]]=btot;
                    blockq[t++]=son[blockq[h]][i];
                }
            //printf("capital(%d)=%d\n",btot,fa[treeq[h]]);
            capital[btot++]=max(fa[treeq[h]],1);

            size[fa[treeq[h]]]=0;
            son[fa[treeq[h]]].clear();
        }
    }
    blockq[0]=0;
    --btot;
    for(int h=0,t=1;h!=t;++h)
        for(int i=son[blockq[h]].size();i--;){
            bnum[son[blockq[h]][i]]=btot;
            blockq[t++]=son[blockq[h]][i];
        }
    printf("%d\n",btot);
    for(int i=1;i<n;++i)printf("%d ",bnum[i]);
    printf("%d\n",bnum[n]);
    for(int i=1;i<btot;++i)printf("%d ",capital[i]);
    printf("%d\n",capital[btot]);
}

你可能感兴趣的:(bfs,分块,dfs序)