题目链接

https://www.lydsy.com/JudgeOnline/problem.php?id=1086

思路

对一棵树进行分块。
dfs,找到了一个节点,记录一个数组st,代表它和它的子树中,没有被划分进任何省的点。
首先对他的每个节点dfs,把它儿子的st序列加入自己的,如果自己st序列大于了B,就把这所有的点都划进一个省里面,省会是当前dfs到的这个节点。
最后再把自己加入st序列中。
但是dfs完了之后,根节点的st序列还是没有被划分到任何一个省中,这时,把它们划进最后一个被添加的省中。
显然,每个省必定会有多于B个节点。
那么如何证明每个点会少于3B个节点呢?
考虑划分省份的流程,dfs到了一个点,它们的子树必定:
1. 有一个子树最大,大小小于等于B
证明:如果这个最大的子树大小大于B,那么在dfs这个子树的根时,就已经产生了大小至少为B而未分配省份的序列,不符合步骤要求。
2. 除了上述子树,其余子树大小之和小于B
证明:如果其余的子树大小之和大于等于B,那么在获得最后一个子树前,其余子树已经达到了分配省份的标准。

所以有:除了最后一个含有根的省份,一个省份的大小必定小于等于2B1
考虑最后剩余的未分配的点,它们的大小必定小于等于B,所以最后一个省份大小必定小于3B

代码

#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;
}