BZOJ 1086 [SCOI2005]王室联邦 树分块

题意:链接

方法:树分块

解析:

为了去刷莫队上树所以来学习树分块,听说这是裸题所以跑来搞。

树分块的过程是什么?

从树根向下递归搜索,如果回溯的节点超过了我们想分成的块的大小(不妨设为a),就将这些节点作为一个块,并且回溯到的这个节点是与该块中的任意节点连通的。

这里显然可以用一个栈来处理。

并且需要注意的是,如果我们不加以限制,会出现什么结果呢?会使分的块中的元素碎成渣,即瞎分。

什么限制呢?

是对于每个节点的相对栈底的限制。

如果在当前节点,其有两个儿子。

在左儿子的子树中搜得的节点数是a-1的话,这时候我们去搜右儿子,不妨假设右儿子为根的子树是一条长为10000的链。

那么显然最下面的叶节点跟左儿子代表的子树是在同一块中的,这显然碎成渣了。

所以每一次搜到一个节点,我们都要把当前的栈的指针当做该节点的相对栈底,这样的话就能避免这个问题。

如上内容请自行脑补。

这样就完了吗?

并没有。

最后的栈中还会剩很多点,但是不会超过b,而我们分的块的大小也不会超过2b(自行脑补),所以把栈里的所有点扔到最后一块里,大小不会超过3b,正好对应题中的要求。

代码:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 1010
using namespace std;
int sta[N];
int n,b,cnt;
int head[N];
int root[N];
int belong[N];
bool v[N];
int tot,top;
struct node
{
    int from,to,next;
}edge[N<<1];
void init()
{
    memset(head,-1,sizeof(head));
    cnt=1;
}
void edgeadd(int from,int to)
{
    edge[cnt].to=to;
    edge[cnt].next=head[from];
    head[from]=cnt++;
}
void dfs(int now)
{
    v[now]=1;
    int bot=top;
    for(int i=head[now];i!=-1;i=edge[i].next)
    {
        int to=edge[i].to;
        if(v[to])continue;
        dfs(to);
        if(top-bot>=b)
        {
            tot++;
            root[tot]=now;
            do
            {
                belong[sta[top]]=tot; 
                top--;
            }while(top!=bot);
        }
    }
    sta[++top]=now;
}
int main()
{
    init();
    scanf("%d%d",&n,&b);
    for(int i=1;i<n;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        edgeadd(x,y);
        edgeadd(y,x);
    }
    dfs(1);
    while(top)
    {
        belong[sta[top--]]=tot;
    }
    cout<<tot<<endl;
    for(int i=1;i<n;i++)cout<<belong[i]<<' ';
    cout<<belong[n]<<endl;
    for(int i=1;i<tot;i++)cout<<root[i]<<' ';
    cout<<root[tot]<<endl; 
}

你可能感兴趣的:(递归,搜索,栈)