codeforces-765E-Tree Folding

呂振麒 bio photo By 呂振麒 Comment

傳送門:

Tree Folding

題意:

給一棵無根樹,每個節點下長度相同的鏈可以合併,最後把樹併成一條最短的鏈,求鏈長度。
若無法合併成一條鏈,輸出-1。

合併示意圖

思路:

這題看第一眼覺得很麻煩也來不及刻出來,賽後我在 comments 區發現一個 keypoint。

把樹直徑的中點當root

如此一來,麻煩的合併操作在子樹內進行,此題分成兩個小問題來解決:

找出樹直徑的中點
合併操作

1. 找出樹直徑的中點

我使用比較簡單的3次dfs方式,第一次dfs隨便挑一個點找最深處,第二次dfs從上次的最深處開始邊走邊給各點上距離標記,一樣找最深處,第三次dfs再從上次的最深處開始,找到直徑上的中點。
兩次dfs找的最深處即是樹直徑的兩端點,維護距離標記是為了找出距離兩端點等長的地方(中點)。
對應下方code,第一二次 dfs 是 dfs1,第三次則是 dfs2

2. 合併相同長度的鏈

我的做法是一邊爬樹一邊維護目前的鏈的長度,每個節點用map記下子樹鏈長的種類,回傳合併後的長度或不可合併(-1)
對應下方code的merge

最後從直徑中點來合併各個子樹,把鏈長搖一搖AC

code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
//hi~ :)
#include <bits/stdc++.h>
#define X first
#define Y second
using namespace std;
typedef pair<int,int> pii;
typedef vector<int> vi;

int N,h[200005],a,b;
vi G[200005];
map<int, int> child[200005];

void init(){
  scanf("%d", &N);
  for(int i=1;i<N;i++){
    scanf("%d %d", &a, &b);
    --a,--b;
    G[a].PB(b);
    G[b].PB(a);
  }
}
int dfs1(int v, int pa){
  int res = v, tmp;
  for(int i : G[v])if(i!=pa){
    h[i] = h[v]+1;
    tmp = dfs1(i, v);
    if(h[tmp] > h[res])
      res = tmp;
  }
  return res;
}
int dfs2(int v, int pa, int dep){
  if(abs(dep-h[v])<2)
    return v;
  for(int i:G[v]) if(i!=pa){
    int res = dfs2(i, v, dep+1);
    if(res != -1) return res;
  }
  return -1;
}
int merge(int v, int pa){
  int res;
  for(int i:G[v])if(i!=pa){
    res = merge(i, v);
    if( res == -1) return res;
    child[v][res] ++;
  }
  if(child[v].size() > 1) return -1;
  auto i = child[v].begin();	// 判斷有無子樹
  return ( (i == child[v].end()) ? (1) : (1+(*i).X) );
}
void sol(){
  int p1, p2, mp, tmp, res=0;
  p1 = dfs1(0, 0);
  h[p1] = 0;
  p2 = dfs1(p1, p1);
  mp = dfs2(p2, p2, 0);

  for(int i : G[mp]){ // merge all subtree of mid point
    tmp = merge(i, mp);
    if(tmp == -1){
      puts("-1");
      return;
    }
    child[mp][tmp] ++;
  }
  if(child[mp].size() > 2){
    puts("-1");
    return;
  }
  for(pii i : child[mp])
    res += i.X;
  while(~res & 1) res>>=1;
  printf("%d\n", res);
  return ;
}

int main(){
  init();
  sol();
}
comments powered by Disqus