[BZOJ1086][Luogu2325]王室联邦--(树上分块)
1.ORZ
orz一下分块算法,orz一下vfk大佬(本题参考了vfk大佬的随笔)。
2.bzoj传送门,luogu传送门。
3.题目大意:
本题其实特别有意思,我是在做一道叫糖果公园的题时,翻vfk的博客时被推荐先做的这道题。
回到正题,首先在一个有n(n<=1000)座城市的国家中,它由n-1条道路构成一棵树,国王要求划分省份,给定一个B(B<=n),要求在每个省中的城市数大于等于B,小与等于3B,并且对于每一个省需要规定一个省会(可以不再省内,且同一座城可用做多个省会),
对于一个省的每座城市,从其到其所在省的省会上的路径上的所有城市应均与其同省,求划分省数,省会,以及每座城市所在省。
4.解题思路:
总体思路:DFS。
首先先选任意一个节点为根节点开始DFS,对于一个节点u的DFS,先去搜其儿子vi,若这时返回的等待序列长度大于等于B,便可以划分一个省份,等扫完所有儿子后再将自己加入等待序列,。但会有一个问题,到了最后返回到根节点,等待序列中的个数可能小于B,但不用担心,我们可以直接把剩下的序列中的城市直接加入最后一个划分的省份,证明如下:显然在之前每个省份的城市数<2B,而最后等待序列中的城市数<B,所以把剩余的加入后,最后一个省份的城市数也会小于3B。
5.贴代码啦!
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define For(i,j,k) for(int i=j;i<=k;++i)
#define Dor(i,j,k) for(int i=j;i>=k;--i)
#define Ror(i,x) for(int i=head[x];i;i=e[i].nxt)
#define fin(s) freopen(s".in","r",stdin)
#define fout(s) freopen(s".out","w",stdout)
using namespace std;
const int N=1e3+10;
struct edge{
int to,nxt;
}e[N<<1];
int tot,head[N];
inline void add(int x,int y){
e[++tot]=(edge){y,head[x]},head[x]=tot;
e[++tot]=(edge){x,head[y]},head[y]=tot;
}
int n,B,bot,siz,st[N],cnt,rt[N],b[N];
void dfs(int x,int f){
int bot=siz;
Ror(i,x){
int to=e[i].to;
if(to==f)continue;
dfs(to,x);
if(siz-bot>=B){
rt[++cnt]=x;
while(siz>bot)b[st[siz--]]=cnt;
}
}
st[++siz]=x;
}
int main(){
fin("luogu2325");
fout("luogu2325");
int x,y;
scanf("%d%d",&n,&B);
For(i,1,n-1){
scanf("%d%d",&x,&y);
add(x,y);
}
dfs(1,1);
while(siz)b[st[siz--]]=cnt;
printf("%d\n",cnt);
For(i,1,n)printf("%d ",b[i]);
puts("");
For(i,1,cnt)printf("%d ",rt[i]);
return 0;
}
6.总结:
树上分块是树上莫队的基础,因此将这题吃透很重要。