1. 程式人生 > >「日常訓練與知識學習」樹的分塊(王室聯邦,HYSBZ-1086)

「日常訓練與知識學習」樹的分塊(王室聯邦,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;
}