還有33天就要高考了,我在幹啥……ios
一棵有根樹,每一個節點有權值。
要求把全部節點分紅組,具備祖先-後代關係的兩個節點不能被分到同一組。
每一組的代價是所包含的節點的最大權值,最小化全部組的代價之和。數據結構
想了半天的樹剖也沒想出來,放棄夢想去看題解……(你怎麼不先想一想部分分啊喂)
發現是啓發式合併。優化
考慮一條鏈(1號節點在中間的某個位置)咋作。
這棵樹的形狀是1號節點下面掛着兩條長鏈。隸屬於同一條鏈的節點都不能放在一組。那麼只須要把兩條鏈各自的最大值節點放到一組,各自的次大值節點放到一組……一條鏈被用完了,另外一條中剩下的節點分別自成一組。spa
那麼假如1號節點下面有更多條鏈呢?只須要合併完兩條以後再把第三條合併進去,方法和上面相同。code
那麼整棵樹其實也dfs而後對每一個節點這麼合併全部的兒子就行了。這個找最大值再找次大值再找次次大值……的數據結構,顯然用堆。get
如何優化複雜度呢?啓發式合併。把每一個節點的兒子按照對應堆的大小排個序,而後把小的往大的合併。這個啓發式合併吧,和咱們熟知的那個啓發式合併還不太同樣,複雜度很是神奇,合併兩個堆以後新堆的大小是原先較大堆的大小,而合併須要的push、pop操做數是原先較小堆的大小。至關於把較小堆的每一個元素以\(O(\log n)\)的複雜度「刪去」了,「刪去」之後就再也不對總複雜度形成代價了。總共最多「刪去」n個節點,每次複雜度\(O(\log n)\),總複雜度\(O(n\log n)\)。數學
寫代碼的時候會陷入僵局——若要保證複雜度正確,對應堆最大的那個兒子不能對複雜度作出貢獻,也就是你不能動它的堆。然而全合併完以後,那個堆裏面的東西要存在父親節點對應的堆裏面。昨天晚上我懵逼半天以後選擇去睡覺,今天上數學課走神的時候纔想到咋整……給每一個節點設置個「id」,表示對應的堆的編號,這樣堆存的地方不用動,交換父親和最大兒子的id便可。string
#include <cstdio> #include <cmath> #include <cstring> #include <algorithm> #include <iostream> #include <queue> #define space putchar(' ') #define enter putchar('\n') using namespace std; typedef long long ll; template <class T> void read(T &x){ bool op = 0; char c; while(c = getchar(), c < '0' || c > '9') if(c == '-') op = 1; x = c - '0'; while(c = getchar(), c >= '0' && c <= '9') x = x * 10 + c - '0'; if(op) x = -x; } template <class T> void write(T x){ if(x < 0) putchar('-'), x = -x; if(x >= 10) write(x / 10); putchar('0' + x % 10); } const int N = 200005; int n, id[N]; ll w[N], ans; vector <int> son[N]; priority_queue <int> que[N]; bool cmp(int a, int b){ return que[id[a]].size() > que[id[b]].size(); } void dfs(int u){ vector <int> buf; for(auto v: son[u]) dfs(v); sort(son[u].begin(), son[u].end(), cmp); for(auto v: son[u]){ if(que[id[u]].empty()) swap(id[u], id[v]); else{ while(!que[id[v]].empty()){ int u_top = que[id[u]].top(), v_top = que[id[v]].top(); que[id[u]].pop(), que[id[v]].pop(); buf.push_back(max(u_top, v_top)); } for(auto x: buf) que[id[u]].push(x); buf.clear(); } } que[id[u]].push(w[u]); } int main(){ read(n); for(int i = 1; i <= n; i++) read(w[i]), id[i] = i; for(int i = 2, f; i <= n; i++) read(f), son[f].push_back(i); dfs(1); while(!que[id[1]].empty()) ans += que[id[1]].top(), que[id[1]].pop(); write(ans), enter; return 0; }