P2325 [SCOI2005]王室联邦 - 树分块

P2325 [SCOI2005]王室联邦

传送门

题意:

将一棵树分为大小在\([B,3B]\)之间的块,每个块有一个编号\(i\),每种块\(i\)有一个关键点\(P\)(关键点\(P\)可以不再块\(i\)中),所有编号为\(i\)的块内的所有点到\(P\)的路径上不存在其他编号不为\(i\)的块内的点(除关键点\(P\))。其中,\(P\)可以为多种编号的块的关键点。问将这棵树划分的方案。

思路:

树分块。
对树进行DFS,用一个栈保存遍历到的节点,记录对\(u\)的子树\(v\)DFS前后top的差值\(\Delta\),若\(\Delta \geq B\),则将栈中新加入的\(\Delta\)个元素分为同一个块,然后令\(u\)为这个块的关键点。最后DFS结束后将栈内剩余的节点加入最后一个块中。这样分块能够满足题目的所有条件。
关于算法正确性:每次的\(\Delta\)\([B,2B]\)之间,而最后剩余的栈内元素一定在\([0,B)\)之间,所以最后一个块的大小不会超过\([2B,3B)\)个,保证合法。

AC Code:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 1000+100;
int n,b;
struct node{
	int to,nxt;
}e[N<<1];
int head[N],tot;
void add(int u,int v){
	e[++tot].to=v;e[tot].nxt=head[u];head[u]=tot;
}
int s[N],top;
int col[N],sh[N],cnt;
void dfs(int u,int fa){
	int now=top;
	for(int i=head[u];i;i=e[i].nxt){
		int v=e[i].to;
		if(v==fa) continue ;
		dfs(v,u);
		if(top-now>=b) {
			sh[++cnt]=u;
			while(top!=now) col[s[top--]]=cnt;
		}
	}
	s[++top]=u;
}
int main(){
	scanf("%d%d",&n,&b);
	for(int i=1;i<n;i++){
		int u,v;scanf("%d%d",&u,&v);
		add(u,v);add(v,u);
	}
	dfs(1,0);
	while(top) col[s[top--]]=cnt;
	printf("%d\n",cnt);
	for(int i=1;i<=n;i++) printf("%d%c",col[i],i==n?'\n':' ');
	for(int i=1;i<=cnt;i++) printf("%d%c",sh[i],i==n?'\n':' ');
	return 0;
}
posted @ 2018-09-09 12:33  dprswdr  阅读(171)  评论(0编辑  收藏  举报