题目链接
https://www.lydsy.com/JudgeOnline/problem.php?id=1086
思路
对一棵树进行分块。
dfs,找到了一个节点,记录一个数组,代表它和它的子树中,没有被划分进任何省的点。
首先对他的每个节点dfs,把它儿子的序列加入自己的,如果自己序列大于了,就把这所有的点都划进一个省里面,省会是当前dfs到的这个节点。
最后再把自己加入序列中。
但是dfs完了之后,根节点的序列还是没有被划分到任何一个省中,这时,把它们划进最后一个被添加的省中。
显然,每个省必定会有多于个节点。
那么如何证明每个点会少于个节点呢?
考虑划分省份的流程,dfs到了一个点,它们的子树必定:
1. 有一个子树最大,大小小于等于。
证明:如果这个最大的子树大小大于,那么在dfs这个子树的根时,就已经产生了大小至少为而未分配省份的序列,不符合步骤要求。
2. 除了上述子树,其余子树大小之和小于。
证明:如果其余的子树大小之和大于等于,那么在获得最后一个子树前,其余子树已经达到了分配省份的标准。
所以有:除了最后一个含有根的省份,一个省份的大小必定小于等于。
考虑最后剩余的未分配的点,它们的大小必定小于等于,所以最后一个省份大小必定小于。
代码
#include <cstdio>
const int maxn=1000;
int cap[maxn+10],stack[maxn+10],top,n,size,tot,cnt,belong[maxn+10];
int pre[maxn*2+10],now[maxn+10],son[maxn*2+10];
inline int ins(int a,int b)
{
pre[++tot]=now[a];
now[a]=tot;
son[tot]=b;
return 0;
}
int dfs(int u,int fa)
{
int j=now[u],bot=top;
while(j)
{
int v=son[j];
if(v!=fa)
{
dfs(v,u);
if(top-bot>=size)
{
cap[++cnt]=u;
while(top>bot)
{
belong[stack[top--]]=cnt;
}
}
}
j=pre[j];
}
stack[++top]=u;
return 0;
}
int main()
{
scanf("%d%d",&n,&size);
for(register int i=1; i<n; ++i)
{
int a,b;
scanf("%d%d",&a,&b);
ins(a,b);
ins(b,a);
}
dfs(1,0);
if(top)
{
while(top)
{
belong[stack[top--]]=cnt;
}
}
printf("%d\n",cnt);
for(register int i=1; i<n; ++i)
{
printf("%d ",belong[i]);
}
printf("%d\n",belong[n]);
for(register int i=1; i<cnt; ++i)
{
printf("%d ",cap[i]);
}
printf("%d\n",cap[cnt]);
return 0;
}