P2018:消息传递题解——二次扫描与换根
消息传递
题面
题目描述
巴蜀国的社会等级森严,除了国王之外,每个人均有且只有一个直接上级,当然国王没有上级。如果A是B的上级,B是C的上级,那么A就是C的上级。绝对不会出现这样的关系:A是B的上级,B也是A的上级。
最开始的时刻是0,你要做的就是用1单位的时间把一个消息告诉某一个人,让他们自行散布消息。在任意一个时间单位中,任何一个已经接到消息的人,都可以把消息告诉他的一个直接上级或者直接下属。
现在,你想知道:
1.到底需要多长时间,消息才能传遍整个巴蜀国的所有人?
2.要使消息在传递过程中消耗的时间最短,可供选择的人有那些?
输入格式
输入文件的第一行为一个整数N(N≤1000),表示巴蜀国人的总数,假如人按照1到n编上了号码,国王的编号是1。第2行到第N行(共N-1行),每一行一个整数,第i行的整数表示编号为i的人直接上级的编号。
输出格式
文件输出共计两行:
第一行为一个整数,表示最后一个人接到消息的最早时间。
第二行有若干个数,表示可供选择人的编号,按照编号从小到大的顺序输出,中间用空格分开。
样例 #1
样例输入 #1
8
1
1
3
4
4
4
3
样例输出 #1
5
3 4 5 6 7
题解
题意简述:给定一棵树,其中1是树根,要找到一个点将其染色,耗费1的时间,然后在每一个时间内,所有被染色点可以在与其相连的点中选择一个染色,将整棵树都被染完的时间即为这个点的答案。要求出最小的答案,并且指出哪些点的答案最优。
对于此类问题,我们先来思考如果指定了一个点如何求出答案。
首先明确,由于一个点可以向上向下传递,那么此时谁是父亲谁是儿子不重要了,我们只需要知道与这个点连边的点可以被这个点染色即可,那么我们可以将这个点变为树根,显然此时只能向下传递,此时这个问题就很像一个树形DP了,具体的,设
其中
但显然,进行
对于这部分的计算,可以设
其中
有了
进行两次DP即可求出解,需要注意的是,对于根节点
加一的原因是无论是
#define N 2005
int head[N],nxt[N],ver[N],tot=1,f[N],g[N],ans[N],b[N],lst[N],n;
void add(int u,int v){
nxt[++tot]=head[u],ver[head[u]=tot]=v;
}
void dfs1(int u,int fa){
int cnt=0;
lst[u]=fa;
for(int i=head[u];i;i=nxt[i]){
int v=ver[i];
if(v==fa)continue;
dfs1(v,u);
}
for(int i=head[u];i;i=nxt[i]){
if(ver[i]==fa)continue;
b[++cnt]=f[ver[i]];
}
sort(b+1,b+cnt+1);
reverse(b+1,b+cnt+1);
for(int i=1;i<=cnt;i++){
f[u]=max(f[u],b[i]+i);
}
}
void dfs2(int u,int fa){
int cnt=0;
if(fa!=1)b[++cnt]=g[fa];
for(int i=head[fa];i;i=nxt[i]){
int v=ver[i];
if(v==u||v==lst[fa])continue;
b[++cnt]=f[v];
}
sort(b+1,b+cnt+1);
reverse(b+1,b+cnt+1);
for(int i=1;i<=cnt;i++){
g[u]=max(g[u],b[i]+i);
}
cnt=1;b[1]=g[u];
for(int i=head[u];i;i=nxt[i]){
int v=ver[i];
if(v==fa)continue;
b[++cnt]=f[v];
}
sort(b+1,b+cnt+1);
reverse(b+1,b+cnt+1);
for(int i=1;i<=cnt;i++){
ans[u]=max(ans[u],b[i]+i);
}
for(int i=head[u];i;i=nxt[i]){
int v=ver[i];
if(v==fa)continue;
dfs2(v,u);
}
}
int main(){
ios::sync_with_stdio(false);
cin>>n;
for(int i=2;i<=n;i++){
int v;
cin>>v;
add(i,v);add(v,i);
}
dfs1(1,0);
ans[1]=f[1];
for(int i=head[1];i;i=nxt[i]){
int v=ver[i];
dfs2(v,1);
}
int Ans=0x3f3f3f3f;
for(int i=1;i<=n;i++)Ans=min(Ans,++ans[i]);
cout<<Ans<<endl;
for(int i=1;i<=n;i++)if(ans[i]==Ans)cout<<i<<" ";
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!