P2325 [SCOI2005]王室联邦 - 树分块学习小记
https://www.luogu.com.cn/problem/P2325
据说最常用的树分块是由这题得来的。
分块之后可以得到:每块大小大于等于B,小于3B(实际上至多一个大于等于2B)。并且对于每个块,都存在一个点(记为块顶,它不一定在块内),使得这个块的点集并这个点形成的点集联通。
维护一个栈st,设栈顶位置为cur,dfs的时候到每个点记下到达这个点时候栈顶的位置bef。
过程:
- 遍历儿子y,回溯到x。如果当前新加进来的大于等于B(cur-bef\ge B),则将[bef+1,cur]中的全都分成一块。
- x要回溯,把x加入栈顶。
- dfs完之后,如果栈还有剩余,就把剩下的加入最后一个块中。
分析一下:进入子树前,栈中点最多是B-1。从子树出来,栈中点最多是B。于是总共最多为2B-1。栈中若有剩余最多为B-1,加入最后一个块最多为3B-1。
using namespace std;
#include <bits/stdc++.h>
#define N 1005
int n,B;
struct EDGE{
int to;
EDGE *las;
} e[N*2];
int ne;
EDGE *last[N];
void link(int u,int v){
e[ne]={v,last[u]};
last[u]=e+ne++;
}
int st[N],cur;
int bel[N],c[N],k;
void dfs(int x,int fa){
int tmp=cur;
for (EDGE *ei=last[x];ei;ei=ei->las)
if (ei->to!=fa){
dfs(ei->to,x);
if (cur-tmp>=B){
++k;
while (cur>tmp)
bel[st[cur--]]=k;
c[k]=x;
}
}
st[++cur]=x;
}
int main(){
scanf("%d%d",&n,&B);
for (int i=1;i<n;++i){
int u,v;
scanf("%d%d",&u,&v);
link(u,v),link(v,u);
}
dfs(1,0);
while (cur)
bel[st[cur--]]=k;
c[k]=1;
printf("%d\n",k);
for (int i=1;i<=n;++i)
printf("%d ",bel[i]);
printf("\n");
for (int i=1;i<=k;++i)
printf("%d ",c[i]);
printf("\n");
return 0;
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步