题目
n(n<=1e3)个点的树,给定一个B(1<=B<=n)值,
要求把树分成若干个块,每一块代表一个省,大小在[B,3B]之间,
每个省必须有一个省会,这个省会可以位于省内,也可以在该省外。
但是该省的任意一个城市到达省会所经过的道路上的城市(除了最后一个城市,即该省省会)都必须属于该省。
一个城市可以作为多个省的省会。
要求输出块数,每个点属于的块号,每个块的省会。
思路来源
https://www.luogu.com.cn/problem/solution/P2325
题解
省会的要求,至少与一个省内的一个点相连。
感觉看了题解之后,是先刻意这么构造,才有的这道题……
先dfs,对于u的儿子v,
如果存在几个儿子v回溯大小之和>=B,则将其分为一块,
考虑u的第一个儿子v的sz[v]=B-1,回溯到u时不会处理,
第二个儿子v的sz[v]=B,于是回溯到u处时会被统计,即块sz<=2*B-1<3B
而这样分,最后根节点1的一些子树v之和sz<B,此时加上根节点1,总sz<=B,
如果sz=B,可以将其新分一块;
如果sz=B-1,可以将其与<=2*B-1的上一块合并,因为省会相同
事实上,注意到2*B-1+B=3*B-1<3*B,
所以,直接将这一小块与刚才最后合法的一大块合并即可,
特判这一小块是第一块的情形,
由于B<=n,故一定有解
代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e3+10;
#define pb push_back
int n,b,u,v;
int dfn[N],c;
int cnt,rt[N],bel[N];
vector<int>e[N];
void dfs(int u,int fa){
int now=c;
for(int i=0;i<e[u].size();++i){
int v=e[u][i];
if(v==fa)continue;
dfs(v,u);
//若干个v子树的大小的和
if(c-now>=b){
rt[++cnt]=u;
for(;c>now;--c){
bel[dfn[c]]=cnt;
}
}
}
dfn[++c]=u;//后序
}
int main(){
scanf("%d%d",&n,&b);
for(int i=2;i<=n;++i){
scanf("%d%d",&u,&v);
e[u].pb(v);e[v].pb(u);
}
dfs(1,-1);
if(!cnt)rt[++cnt]=1;//如果1还没有当省会 就开一个
for(;c;--c){//把根节点1及最后的点都放入1最后的一个省会的集合里
bel[dfn[c]]=cnt;
}
printf("%d\n",cnt);
for(int i=1;i<=n;++i){
printf("%d%c",bel[i]," \n"[i==n]);
}
for(int i=1;i<=cnt;++i){
printf("%d%c",rt[i]," \n"[i==cnt]);
}
return 0;
}