[scoi2005]王室联邦 解题报告

本文详细介绍了如何使用树分块技术解决[scoi2005]王室联邦的问题。通过按DFS序或BFS序处理,确保每个块内的节点到最近公共祖先的路径不超过3B个节点,最终形成大小在[0, 3B)范围内的块。这种方法结合DFS和BFS策略,有效优化了问题的求解效率。" 124557706,9200420,使用PyCharm部署Flask项目到远程服务器,"['Python', 'Flask', '服务器部署', 'Docker', 'PyCharm']

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

拿来学了一下树分块。
树分块的要求是把树分成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]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值