题目链接:
https://www.luogu.org/problemnew/show/P2325
题意:
给你一个含 n n n个点的树,让你给树分块,每一块之间的点要不经过其他块而连通,每一块的大小 S S S,保证 B < = S < = 3 B B<=S<=3B B<=S<=3B,输出可以分成的块数,并输出编号 i i i节点对应块编号,输出每个块的中的任意一个节点;
分析:
推荐一篇写的很好的博客:http://old.orzsiyuan.com/articles/problem-SCOI-2005-Royal-Commonwealth/
建议模拟一下这就是单个块最大可能节点数,整个
d
f
s
dfs
dfs过程由最左边转移到中间,然后遍历结束中间那颗子树,此时栈里相对来说剩下
2
B
−
1
2B-1
2B−1个节点,然后便将这
2
B
−
1
2B-1
2B−1个分成一块,当访问第三课子树时,剩下不足B个,将根节点入栈后便退出
d
f
s
dfs
dfs,然后将栈里剩余的B个分给之前的最后一块,那么最后一块就是
3
B
−
1
3B-1
3B−1个;(仅供个人理解)
听说是专门为学习树上莫队而生的习题;
代码:
// luogu-judger-enable-o2
#include <iostream>
#include<cstring>
#include<string>
#include<cstdio>
using namespace std;
#define maxn 1009
struct edge{
int u,v,next;
}e[2*maxn];
int cnt,head[maxn];
void add(int u,int v){
e[cnt].u=u,e[cnt].v=v;
e[cnt].next=head[u];
head[u]=cnt++;
}
int top,n,b,num[maxn],cot,st[maxn],sc[maxn];
void dfs(int x,int fa){
int now=top;
for(int i=head[x];i!=-1;i=e[i].next){
int v=e[i].v;
if(v==fa)continue;
dfs(v,x);
if(top-now>=b){
sc[++cot]=x;
while(top!=now)
num[st[top--]]=cot;
}
}
st[++top]=x;
}
int main()
{
cin>>n>>b;
int a,b;
memset(head,-1,sizeof(head));
for(int i=0;i<n-1;i++){
scanf("%d%d",&a,&b);
add(a,b);
add(b,a);
}
dfs(1,0);
while(top)num[st[top--]]=cot;
cout<<cot<<endl;
for(int i=1;i<=n;i++)
cout<<num[i]<<" ";
cout<<endl;
for(int i=1;i<=cot;i++)
cout<<sc[i]<<" ";
return 0;
}
我们坚持一件事情,并不是因为这样做了会有效果,而是坚信,这样做是对的。
——哈维尔