thr31 ♥10 5888213086♥9 admin♥4

题意

这道题题目有点绕,实际就是找出一种方案,使得从根节点出发的路径的最大值最小,答案即该最大值的最小值。

贪心

这道题的先进滑道是可以贪心建造的。

d p [ x ] dp[x] 为从 x x 出发往下的路径在最优情况下的最大值。

对于一个节点 x x ,假设它的儿子的子树全部都已经为最优方案,那么为了使 x x 出去的路径的最大值最小,对于所有 x x 的儿子 y y ,一定要把先进滑道通向 d p [ y ] dp[y] 最大的儿子,否则 d p [ y ] dp[y] 最大的儿子传到 x x 的值一定比当前大。

因此一个节点的先进滑道一定是连向dp值最大的儿子。

树形DP

有了上一步的贪心,可以发现,求每个节点的dp值,必须要所有它的儿子的dp值,因此用树形dp求解。

由于一条全是先进滑道的链(以下称作先进链)的权值只能通过该先进链的长度求取,并不能拆成几段分开求解。因此从一个点往下的所有先进链上的点都要存下来。

由于每个点只会向一个儿子连出一条先进边,根据启发式合并的思想,应该把父亲加到儿子的 v e c t o r vector 中,再把父亲指向儿子的 v e c t o r vector 。这样每个点加一条先进边复杂度是 O ( 1 ) O(1) 的。

但是,如果直接把 v e c t o r vector 中的所有点都把父亲更新一遍,显然会超时。

考虑 b = log 2 a b=\lceil \log_2a \rceil
a = a + 1 a=a+1 时,只有 a a 为二的幂或零时, b b 才会+1。

因此,我们只需在把离儿子节点距离为 2 k 2^k 0 0 的位置的值重新更新给父亲,其它都沿用儿子的dp值即可。

这样每个点都只从 l o g log 个点更新。
总复杂度为 O ( n × l o g   n ) O(n \times log\ n)

代码

  1. #include<bits/stdc++.h>
  2. #define M 100005
  3. using namespace std;
  4. void Max(int &a,int b){
  5. b>a&&(a=b);
  6. }
  7. int hd[M],nxt[M+M],to[M+M],cnte;
  8. void addedge(int a,int b){
  9. nxt[++cnte]=hd[a];
  10. to[cnte]=b;
  11. hd[a]=cnte;
  12. }
  13. int n;
  14. int p[M],dp[M];
  15. vector<int> S[M];
  16. void dfs(int x,int f){
  17. int Son=0,mx=-1;
  18. for(int i=hd[x];i;i=nxt[i]){
  19. int y=to[i];
  20. if(y==f)continue;
  21. dfs(y,x);
  22. if(dp[y]>dp[Son])Son=y;
  23. }
  24. for(int i=hd[x];i;i=nxt[i]){
  25. int y=to[i];
  26. if(y==f||y==Son)continue;
  27. Max(mx,dp[y]);
  28. }
  29. if(!Son){
  30. S[x].push_back(0);
  31. dp[x]=0;
  32. }
  33. else {
  34. p[x]=p[Son];
  35. dp[x]=max(mx+1,dp[Son]);
  36. int sz=S[p[x]].size();
  37. Max(dp[x],S[p[x]][sz-1]+1);//从零更新
  38. for(int i=0;(1<<i)<sz;i++)
  39. Max(dp[x],S[p[x]][sz-1-(1<<i)]+i+2);//从二的幂更新
  40. S[p[x]].push_back(mx+1);
  41. }
  42. }
  43. void init(){
  44. memset(dp,0,sizeof(dp));
  45. dp[0]=-1;
  46. cnte=0;
  47. for(int i=1;i<=n;i++){
  48. S[i].clear();
  49. hd[i]=dp[i]=0;
  50. p[i]=i;
  51. }
  52. }
  53. void solve(){
  54. scanf("%d",&n);
  55. init();
  56. for(int i=1;i<n;i++){
  57. int a,b;
  58. scanf("%d%d",&a,&b);
  59. addedge(a,b);
  60. addedge(b,a);
  61. }
  62. dfs(1,0);
  63. printf("%d\n",dp[1]);
  64. }
  65. int main(){
  66. int T;
  67. scanf("%d",&T);
  68. while(T--)solve();
  69. return 0;
  70. }
点赞