题目
H国的社会等级森严,除了国王之外,每个人均有且只有一个直接上级,当然国王没有上级。如果A是B的上级,B是C的上级,那么A就是C的上级。绝对不会出现这样的关系:A是B的上级,B也是A的上级。
最开始的时刻是0,你要做的就是用1单位的时间把一个消息告诉某一个人,让他们自行散布消息。在任意一个时间单位中,任何一个已经接到消息的人,都可以把消息告诉他的一个直接上级或者直接下属。
现在,你想知道:
1.到底需要多长时间,消息才能传遍整个H国的所有人?
2.要使消息在传递过程中消耗的时间最短,可供选择的人有那些?
$Solution$
这里主要介绍一个$O(n\log_2n)$的算法
虽然对于这道题$O(n^2\log_2n)$暴力枚举根已经可以$AC$了,但优化是无止境的
首先可以想出这道题对于一个确定的根的$dp$
再以任意点$root$为主根前提下
我们设$f_{i}$表示以$i$为根不计算根被传递到消息时间,把消息传到$i$为根的子树的最短时间
当$i$的子树均被计算完毕时
$\displaystyle{f_i=\max_{j\in son_i}}\{f_j+order_j\}$
其中$order_j$表示$j$是$i$的孩子中第$order_j$个选的,也就是他在$i$收到消息后第$order_j$个时间收到消息的
很容易想到贪心,我们让$f_j$值大的先传
单次复杂度$O(n\log_2n)$
但这样做需要枚举根,最终复杂度$O(n^2\log_2n)$
瓶颈在于什么,仔细思考我们会发现其实是我们确定了向“下”传的顺序,而因此很多子树的信息在不同的根下实际上是一样的
什么意思,我们看一下这幅图
在$fa$,$i$,$brother1$,$brother2$作根的时候,以$j$为根的子树的形态没有一点改变
我们有没有办法一次计算这些信息呢
有的,那就是所谓的二次扫描与换根法
在李煜东的书里介绍了这个算法并给出了一个例题,但这个例题会更为复杂一些
我先重复一下这个算法流程
- 一次树形dp,求出每个点以$root$为根的子树中信息$down$
- 一次DFS求出每个点以$root$为根来自父亲的信息$up$,同时合并$up$和$down$
我们用这道题来看如何实现
对于$down$部分是类似的
设$down_{i}$表示不计算根被传递到消息时间,把消息传到$i$为根的子树的最短时间
我们在计算完了$i$的儿子$j$后来更新$i$
$\displaystyle{down_i=\max_{j\in son_i}}\{down_j+order_j\}$
$order$可以直接通过排序得到
1 | inline void tree_dp(re int x){ |
2 | re int i,y,res=0;re vector<int> son; |
3 | for(i=h[x];i;i=e[i].next){ |
4 | y=e[i].to; |
5 | tree_dp(y); |
6 | son.push_back(dpson[y]); |
7 | } |
8 | sort(son.begin(),son.end(),cmp); |
9 | for(i=0;i<(int)son.size();++i)res=max(res,son[i]+i+1); |
10 | dpson[x]=res; |
11 | } |
而对于$up$部分
设$up_{i}$表示不计算根被传递到消息时间,把消息传到除了$i$为根的子树的节点的最短时间
可能很难理解,我们用一张图来展示
可以形象的理解为$down$对应的部分是在有根情况下向“下”的很多支子树,而$up$对应的部分是向“上”的那一支
我们在计算完了$j$的父亲$i$后来更新$j$
$\displaystyle{up_j=\max\{up_i+{order’}_{i}~,\max_{k\in son_i\vee k\ne j}}~\{down_k~+~order_{k}’\}~\}$
用这幅图理解一下,$up_j$保存的是来自父亲$i$的一支,其中包含了他父亲的父亲那一支$up_j$,也包含了$i$的父亲除了$i$的其他儿子支
注意一下:为了处理方便,我们并不一开始显式去掉$i$的儿子$j$,而是吧$down_k$和$up_i$加进去排序,在需要求出$up_j$时我们二分查找$j$的位置,并把这个位置后面的位置的$order$值减一,因为我们在我们一开始的假设中,选$j$实际上是花了一个时间的,但实际并没有.实际操作上可以利用前缀和后缀$\max$来实现
1 | inline void change_root(re int x){ |
2 | re int i,y,pos;re vector<int> son; |
3 | for(i=h[x];i;i=e[i].next){y=e[i].to;son.push_back(dpson[y]);}if(fa[x])son.push_back(dpfa[x]); |
4 | sort(son.begin(),son.end(),cmp); |
5 | for(i=0;i<(int)son.size();++i)t[i]=son[i]+i+1; |
6 | *maxl=t[0];for(i=1;i<(int)son.size();++i)maxl[i]=max(maxl[i-1],t[i]); |
7 | maxr[son.size()-1]=t[son.size()-1];for(i=son.size()-2;i>=0;--i)maxr[i]=max(maxr[i+1],t[i]); |
8 | reverse(son.begin(),son.end()); |
9 | for(i=h[x];i;i=e[i].next){ |
10 | y=e[i].to; |
11 | pos=lower_bound(son.begin(),son.end(),dpson[y])-son.begin();pos=son.size()-pos-1; |
12 | if(!pos)dpfa[y]=max(0,maxr[1]-1); |
13 | else if(pos==(int)son.size()-1)dpfa[y]=max(0,maxl[pos-1]); |
14 | dpfa[y]=max(maxl[pos-1],maxr[pos+1]-1); |
15 | } |
16 | dp[x]=maxl[son.size()-1];for(i=h[x];i;i=e[i].next){y=e[i].to;change_root(y);} |
17 | } |
其他细节比如边界什么的需要做了才知道
1 |
|
2 |
|
3 |
|
4 |
|
5 | using namespace std; |
6 | struct read{ |
7 | char buf[1<<25],*opt; |
8 | read():opt(buf){buf[fread(buf,1,sizeof buf,stdin)]=0;} |
9 | template<typename _int> |
10 | operator _int(){ |
11 | _int res=0,flag=1; |
12 | while(*opt<48&&*opt!='-')++opt; |
13 | if(*opt=='-'){flag=-1;++opt;} |
14 | while(*opt>32)res=(res<<1)+(res<<3)+(*opt++)-48; |
15 | return res*flag; |
16 | } |
17 | }it; |
18 | inline char cmp(re int x,re int y){return y<x;} |
19 | struct Edge{int to,next;}e[N<<1]; |
20 | int n,cnt,h[N],t[N],dpson[N],dpfa[N],maxl[N],maxr[N],fa[N],dp[N],ans=INF,Ans[N]; |
21 | inline void AddEdge(re int x,re int y){e[++cnt]=(Edge){y,h[x]};h[x]=cnt;} |
22 | inline void tree_dp(re int x){ |
23 | re int i,y,res=0;re vector<int> son; |
24 | for(i=h[x];i;i=e[i].next){ |
25 | y=e[i].to; |
26 | tree_dp(y); |
27 | son.push_back(dpson[y]); |
28 | } |
29 | sort(son.begin(),son.end(),cmp); |
30 | for(i=0;i<(int)son.size();++i)res=max(res,son[i]+i+1); |
31 | dpson[x]=res; |
32 | } |
33 | inline void change_root(re int x){ |
34 | re int i,y,pos;re vector<int> son; |
35 | for(i=h[x];i;i=e[i].next){y=e[i].to;son.push_back(dpson[y]);}if(fa[x])son.push_back(dpfa[x]); |
36 | sort(son.begin(),son.end(),cmp); |
37 | for(i=0;i<(int)son.size();++i)t[i]=son[i]+i+1; |
38 | *maxl=t[0];for(i=1;i<(int)son.size();++i)maxl[i]=max(maxl[i-1],t[i]); |
39 | maxr[son.size()-1]=t[son.size()-1];for(i=son.size()-2;i>=0;--i)maxr[i]=max(maxr[i+1],t[i]); |
40 | reverse(son.begin(),son.end()); |
41 | for(i=h[x];i;i=e[i].next){ |
42 | y=e[i].to; |
43 | pos=lower_bound(son.begin(),son.end(),dpson[y])-son.begin();pos=son.size()-pos-1; |
44 | if(!pos)dpfa[y]=max(0,maxr[1]-1); |
45 | else if(pos==(int)son.size()-1)dpfa[y]=max(0,maxl[pos-1]); |
46 | else dpfa[y]=max(maxl[pos-1],maxr[pos+1]-1); |
47 | } |
48 | dp[x]=maxl[son.size()-1];for(i=h[x];i;i=e[i].next){y=e[i].to;change_root(y);} |
49 | } |
50 | int main(void){ |
51 | n=it; |
52 | re int i; |
53 | for(i=2;i<=n;++i){fa[i]=it;AddEdge(fa[i],i);} |
54 | tree_dp(1);change_root(1); |
55 | for(i=1;i<=n;++i)if(dp[i]<ans){ans=dp[i];Ans[*Ans=1]=i;}else if(ans==dp[i])Ans[++*Ans]=i; |
56 | printf("%d\n",ans+1); |
57 | for(i=1;i<=*Ans;++i)printf("%d ",Ans[i]); |
58 | return 0; |
59 | } |