正题
题目链接:https://jzoj.net/senior/#contest/show/3005/1
题目大意
一棵树,一个信息开始给一个人,每次得到信息的人可以选择相邻节点中的一个传递,求最短多久可以传到所有人。
解题思路
我们先考虑如何求一根的答案,farifar_ifari表示第iii个点的传递玩子树所需要的最短时间,显然对于子节点的farfarfar排个序依次传递就好了。
之后考虑换根,对于一个节点我们要求出从这个父节点不需要传递该节点时的最短时间upiup_iupi,这个值在父节点操作时可以求出。
对于dpdpdp到的节点xxx,我们将子节点的farfarfar和upxup_xupx丢进去排序,然后用一个前缀maxmaxmax和一个后缀maxmaxmax求出来所有子节点的upupup还可以顺便求出该点答案。
时间复杂度:O(nlogn):O(n\log n):O(nlogn)
codecodecode
注意这里并没有使用upupup数组,而是覆盖掉farfarfar数组来使用
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
const int N=2e5+10;
struct node{int to,next;
}a[N*2];
int tot,ls[N],pre[N],aft[N];
int n,far[N],ans[N],answer;
vector<int> q[N];
void addl(int x,int y){a[++tot].to=y;a[tot].next=ls[x];ls[x]=tot;return;
}
bool cmp(int x,int y)
{return far[x]>far[y];}
void dfs(int x,int fa){int maxx=0,maxy=0;for(int i=ls[x];i;i=a[i].next){int y=a[i].to;if(y==fa) continue;dfs(y,x);q[x].push_back(y);}sort(q[x].begin(),q[x].end(),cmp);for(int i=0;i<q[x].size();i++)far[x]=max(far[q[x][i]]+i+1,far[x]);return;
}
int dp(int x,int fa){if(fa)q[x].push_back(x);sort(q[x].begin(),q[x].end(),cmp);for(int i=1;i<=q[x].size();i++)pre[i]=max(pre[i-1],far[q[x][i-1]]+i);for(int i=q[x].size();i>=1;i--)aft[i]=max(aft[i+1],far[q[x][i-1]]+i);ans[x]=max(ans[x],pre[q[x].size()]);answer=min(answer,ans[x]);for(int i=0;i<q[x].size();i++){int y=q[x][i],z;far[y]=max(pre[i],aft[i+2]-1); }for(int i=ls[x];i;i=a[i].next){int y=a[i].to;if(y==fa) continue;dp(y,x);}
}
int main()
{freopen("news.in","r",stdin);freopen("news.out","w",stdout);scanf("%d",&n);for(int i=2;i<=n;i++){int x;scanf("%d",&x);addl(i,x);addl(x,i);}answer=2147483647;dfs(1,1);dp(1,0);printf("%d\n",answer+1);for(int i=1;i<=n;i++)if(ans[i]==answer)printf("%d ",i);return 0;
}