題意:
思路:
首先我們dfs,如果一個節點v的兒子u所在的子樹大於等於B,那麼就把這個子樹當作一個省份,對於這種情況,省會是u,v其實都可以。如果一個兒子u所在的子樹小於B,可以先暫存起來,繼續遍歷其他兒子,當暫存的數量大於等於B的時候,將暫存的都歸爲一個省,省會爲v,因爲會暫存的子樹大小爲B-1,所以這樣產生的子樹大小最大爲2B-2,在處理完所有子樹,就把v也加入到暫存的地方返回去,這裏能看出,一個棧很適合處理這個過程。
這樣一路處理下來,只剩下一個問題,最後,我可能還剩下一個包含根節點的最大B-1個節點的暫存節點。因爲我們之前說子樹最大爲2B-2,所以直接把剩下的暫存節點歸到上一個省份裏面即可(記得改省會)
我們可以注意到,我們這樣的分塊其實只能保證塊大小,不能保證連通性
錯誤及反思:
有人說是樹上莫隊前置題目,主要是爲了理解樹上分塊的大致思路。
代碼:
#include<bits/stdc++.h>
using namespace std;
const int N = 1100;
vector<int> son[N];
int n,b;
int ans[N],sh[N],tot=1;
stack<int> s;
void dfs(int now,int fa){
int si=s.size();
for(int i=0;i<son[now].size();i++){
if(son[now][i]!=fa)
{
dfs(son[now][i],now);
//if(now==6) printf("#%d\n",s.size()-si);
if(s.size()-si>=b){
while(s.size()!=si){
ans[s.top()]=tot;
s.pop();
}
sh[tot++]=now;
}
}
}
s.push(now);
}
int main(){
scanf("%d%d",&n,&b);
for(int i=0;i<n-1;i++)
{
int ta,tb;
scanf("%d%d",&ta,&tb);
son[ta].push_back(tb);
son[tb].push_back(ta);
}
dfs(1,1);
while(!s.empty()){
ans[s.top()]=tot-1;
s.pop();
}
sh[tot-1]=1;
tot--;
printf("%d\n",tot);
for(int i=1;i<=n;i++) printf("%d ",ans[i]);
puts("");
for(int i=1;i<=tot;i++) printf("%d ",sh[i]);
}