「日常训练&知识学习」树的分块(王室联邦,HYSBZ-1086)

题意与分析

这题的题意就是树分块,更具体的看题目(中文题)。
学习这一题是为了树的分块,为树上莫队做铺垫。
参考1:https://blog.csdn.net/LJH_KOQI/article/details/52326103
参考2:https://blog.csdn.net/popoqqq/article/details/42772237
注意到题目要求某块区域所有的点到根的路径上的点都属于该区域。因此不能够暴力地去dfs,每找到\(B\)个分一块是不可取的,因为无法保证联通性(一颗子树的下半截和另一棵子树的上半截组成一块)。因此,我们需要尽可能地从底部往上去组织块(Block),“每棵子树较深的部分自己成块,然后靠近根的部分组成一个大块”。
因此这么做:对于一个点\(x\),以初次访问它时,栈的栈顶作为相对栈底,每遍历完它的一个子节点所在的子树(先遍历完),判断此时栈顶减去相对栈底得到的元素个数是否\(\ge B\),如果成立,那么弹栈至相对栈顶。当访问完所有子节点要回溯到x的父节点时,再把x压入栈。这样一来,一个子树深搜过后,子树内地未分块节点不会超过B,而搜索子树前的未分块节点数也不会超过b,从而每块不会超过\(2B\);最后dfs结束时剩余的未组成块的节点个数也不会超过b,从而最后一块不会超过\(3B\),把它们归到最后一个块就可以了。这种分块方法就可以保证连通性和块的大小了。

代码

/*
 * Filename: hysbz1086.cpp
 * Date: 2018-11-13
 */

#include <bits/stdc++.h>

#define INF 0x3f3f3f3f
#define PB push_back
#define MP make_pair
#define fi first
#define se second
#define rep(i,a,b) for(repType i=(a); i<=(b); ++i)
#define per(i,a,b) for(repType i=(a); i>=(b); --i)
#define ZERO(x) memset(x, 0, sizeof(x))
#define MS(x,y) memset(x, y, sizeof(x))
#define ALL(x) (x).begin(), (x).end()

#define QUICKIO                  \
    ios::sync_with_stdio(false); \
    cin.tie(0);                  \
    cout.tie(0);
#define DEBUG(...) fprintf(stderr, __VA_ARGS__), fflush(stderr)

using namespace std;
typedef long long ll;
typedef int repType;

const int MAXN=1005;
vector<int> G[MAXN];
int stk[MAXN],top=0;
int root[MAXN],cnt=0;
int belong[MAXN];
int n,b;

void dfs(int now, int pre)
{
    int bottom=top;
    rep(i,0,int(G[now].size())-1) if(G[now][i]!=pre)
    {
        dfs(G[now][i],now);
        if(top-bottom>=b)
        {
            root[++cnt]=now;
            while(top!=bottom)
                belong[stk[top--]]=cnt;
        }
    }
    stk[++top]=now;
}

int
main()
{
QUICKIO
    cin>>n>>b;
    rep(i,1,n-1)
    {
        int u,v; cin>>u>>v;
        G[u].PB(v);
        G[v].PB(u);
    }
    dfs(1,0);
    while(top) // the last block
        belong[stk[top--]]=cnt;
    cout<<cnt<<endl;
    rep(i,1,n)
        cout<<belong[i]<<char(i==n?'\n':' ');
    rep(i,1,cnt)
        cout<<root[i]<<char(i==cnt?'\n':' ');
    return 0;
}
posted @ 2018-11-13 10:49  ISoLT  阅读(130)  评论(0编辑  收藏  举报