LuoguP5290 [十二省聯考2019]春節十二響 | 啟發式合並


還有33天就要高考了,我在干啥……

題目概述

一棵有根樹,每個節點有權值。
要求把所有節點分成組,具有祖先-后代關系的兩個節點不能被分到同一組。
每一組的代價是所包含的節點的最大權值,最小化所有組的代價之和。

題解

想了半天的樹剖也沒想出來,放棄夢想去看題解……(你怎么不先想想部分分啊喂)
發現是啟發式合並。

考慮一條鏈(1號節點在中間的某個位置)咋做。
這棵樹的形狀是1號節點下面掛着兩條長鏈。隸屬於同一條鏈的節點都不能放在一組。那么只需要把兩條鏈各自的最大值節點放到一組,各自的次大值節點放到一組……一條鏈被用完了,另一條中剩下的節點分別自成一組。

那么假如1號節點下面有更多條鏈呢?只需要合並完兩條之后再把第三條合並進去,方法和上面相同。

那么整棵樹其實也dfs然后對每個節點這么合並所有的兒子就好了。這個找最大值再找次大值再找次次大值……的數據結構,顯然用堆。

如何優化復雜度呢?啟發式合並。把每個節點的兒子按照對應堆的大小排個序,然后把小的往大的合並。這個啟發式合並吧,和我們熟知的那個啟發式合並還不太一樣,復雜度非常神奇,合並兩個堆之后新堆的大小是原先較大堆的大小,而合並需要的push、pop操作數是原先較小堆的大小。相當於把較小堆的每個元素以\(O(\log n)\)的復雜度“刪去”了,“刪去”以后就不再對總復雜度造成代價了。總共最多“刪去”n個節點,每次復雜度\(O(\log n)\),總復雜度\(O(n\log n)\)

寫代碼的時候會陷入僵局——若要保證復雜度正確,對應堆最大的那個兒子不能對復雜度做出貢獻,也就是你不能動它的堆。然而全合並完之后,那個堆里面的東西要存在父親節點對應的堆里面。昨天晚上我懵逼半天之后選擇去睡覺,今天上數學課走神的時候才想到咋整……給每個節點設置個“id”,表示對應的堆的編號,這樣堆存的地方不用動,交換父親和最大兒子的id即可。

代碼

#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;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM