點這裡看題目。
感覺自己好蠢
假如我們有兩棵樹\(t_1,t_2\),我們應該怎麼計算出它們合併之後的最優解呢?
設最優情況下,\(t_1\)的所有記憶體段的集合為\(m_1\),\(t_2\)的集合為\(m_2\)。我們可以知道,\(m_1,m_2\)中所有的元素都是不能再合併的(廢話)。
考慮有\(m_1,m_2\in m_1,m_1>m_2\),有\(m_3,m_4\in m_2, m_3>m_4\),我們應該如何分配?我們只能:
1.最大值和次大值組合,得到的記憶體為\(\max\+\max\\);
2.最大值和最大值組合,次大值和次大值組合,得到的記憶體為\(\max\+\max\\)。
很顯然,方案 2 更優,可以通過列舉所有情況證明。
於是我們就可以想到,用堆維護\(m_1\)和\(m_2\)。每次取出兩個集合中的最大值,合併起來,放到合併結果的堆裡面。最後有剩餘的,直接塞進新的堆裡面。
很不幸,這樣做是\(o(\sum_u d_u\times \log_2n)\)的,我覺得會 tle ,其中\(d_u\)為\(u\)的深度。
考慮優化。我們每次其實只需要合併,得到合併過後的堆就好。因此,我們可以直接將較小的堆合併到較大的裡面,然後就得到了合併結果。這就是啟發式合併。
這樣做的時間複雜度\(o(n\log_2n)\)?我怎麼覺得挺像\(o(n\log_2^2n)\)的?
#include #include using namespace std;
typedef long long ll;
const int maxn = 2e5 + 5;
templatevoid read( _t &x )
while( s >= '0' && s <= '9' )
x *= f;
}templatevoid write( _t x )
if( 9 < x )
putchar( x % 10 + '0' );
}template_t max( const _t a, const _t b )
struct edge
graph[maxn << 1];
priority_queueq[maxn];
int stk[maxn], top;
int head[maxn], m[maxn], id[maxn];
int n, cnt;
void addedge( const int from, const int to )
int merg( int x, int y )
void dfs( const int u )
q[id[u]].push( m[u] );
}int main()
春節十二響 十二省聯考2019
給定一顆樹,要求將其上的節點分成若干組,使得每一組的節點互相不擁有祖先 後代關係。定義每一組的值為該組節點權值最大值,求值總和最小值。硬上不是很顯然的貪心,但是資料中鏈的情況給了提示。考慮鏈的情況 對於根節點兩側的鏈,我們分別排序,然後覆蓋選取即可。這個貪心的正確性是顯然的。現在考慮完整的資料 對於...
P5290 十二省聯考2019 春節十二響
傳送門 考慮乙個子樹裡是怎麼劃分的,維護劃分出來的每個集合的最大值,這個可以用乙個 multiset 維護 設 s x 表示節點 x 的子樹中,最優劃分 劃分出來的每個塊的節點最大值 首先葉子節點的集合顯然只有它本身 然後考慮子樹之間的合併,設兩個子樹根節點為 x,y 因為兩個子樹之間一定不會有祖先...
P5290 十二省聯考2019 春節十二響
考試的時候,本來想拿60的貪心,但是只拿了15 很不開心!不過現在知道正解了qwq 對於每個點,都開乙個優先佇列,這個優先佇列裡的值,代表這個點的子樹分成的若干個集合中最大的值。那麼我們對於乙個沒有處理的點,分別列舉每乙個子樹,分別合併每乙個優先佇列,最後再加入這個點,得到新的優先佇列。對於正確性,...