BZOJ4632 樹的編碼

2022-05-09 22:05:02 字數 1914 閱讀 7088

[bzoj4632]樹的編碼

試題描述

shuxk 正在對一棵n個結點的有根樹進行研究,首要的一件事就是對這棵樹進行編碼。

lz 說:「這還不容易嗎?我令根節點的編號為 1,然後保證每個結點的編號都比它的父親結點的編號大。這樣不

就行了嗎?」但 shuxk 對這種編碼方案並不滿意,因為沒什麼特色,從中也得不到什麼有用的資訊。於是他想出

了一種新的編碼,這種編碼需要滿足兩個條件:

1. 作為乙個 oier,應該保證每個結點的編碼是乙個 01 串;

2. 為了體現樹的特點,假設??號結點的編碼為si,應該有如下性質:

如果結點u是結點v的祖先,那麼su就應該是sv的字首;

如果結點u不是結點v的祖先或後代,那麼su就不允許是sv的字首。

lz 說:「 這樣編碼確實有特色。 但是這樣一來,編碼會很長呀!」

shuxk 說:「我們就來求一種最優的編碼,使得所有結點的編碼長度之和最小。」當然, lz 和 shuxk 都已經從 

oi 界退役了,他們討論完了以後就把這個任務交給你了。

輸入

輸入檔案的第一行包含乙個正整數n(n<=10^5),表示棋盤的大小。

第二行包含n - 1個整數, 分別表示2~n號結點的父親結點fi( 保證fi輸出

輸出檔案只有一行乙個整數, 表示所有結點編碼長度之和的最小值。

輸入示例

511

33

輸出示例

6

資料規模及約定

見「輸入

題解

考慮菊花圖,即只有 2 層的情況,那麼就是貪心地合併,每次合併的代價為兩個集合大小總和,合併之後刪掉原來兩個集合,新增乙個新集合,其大小等於兩集合大小總和。仔細想想便知,這裡所花的「代價」即為所用編碼的長度。那麼策略是每次找到兩個最小的集合合併,用堆維護即可。

現在問題是對於乙個節點,它有許多孩子,每個兒子節點 i 有乙個子樹大小 siz[i]。不難發現我們可以繼續上面的合併,即從下而上按照上面的策略合併。乙個子樹內所對應的所有集合合併後大小即為該子樹大小。所以以子樹大小為關鍵字每次找到兩個最小的集合合併,累計答案。

#include #include #include #include #include #include #include #include #include #include #include using namespace std;

const int buffersize = 1 << 16;

char buffer[buffersize], *head, *tail;

inline char getchar()

return *head++;

}int read()

while(isdigit(c))

return x * f;

}#define maxn 100010

#define maxm 200010

int n, m, fa[maxn], head[maxn], next[maxm], to[maxm];

void addedge(int a, int b)

priority_queue q;

int siz[maxn], ans;

void build(int u)

for(int e = head[u]; e; e = next[e]) if(to[e] != fa[u]) q.push(-siz[to[e]]);

while(!q.empty())

return ;

}int main()

bzoj1211 樹的計數 prufer編碼

prufer編碼大概就是將一顆無根樹對應到乙個序列裡面,然後構造就是不斷找最小的度數為1的點然後將他的father加進去。不難發現每個編號的出現次數就是deg 1 includeusing namespace std const int n 300 typedef long long ll ll a...

Huffman樹的編碼解碼

上個學期做的課程設計,關於huffman樹的編碼解碼。要求 輸入huffman樹各個葉結點的字元和權值,建立huffman樹並執行編碼操作 輸入一行僅由01組成的電文字串,根據建立的huffman樹進行解碼操作,程式最後輸出解碼後的結果 huffman.h定義了樹的結點資訊,各種操作。gcc編譯通過...

bzoj 4317 Atm的樹 樹分治

一道比較經典的樹分治把。二分答案,然後在按照點分治後得到的重心樹中找距離 d的點的數量即可。時間複雜度o nlog 3n ac 如下 include include include include define inf 1000000000 define n 30005 define m 12000...