題目鏈結
\(boruvka\)演算法就是先把每個點看作乙個聯通塊,然後不斷在聯通塊之間找最優的邊進行合併。因為每次聯通塊的數量最少縮小一半。所以合併次數是\(log\)的
先把所有的點權掛到\(trie\)樹上。然後對於每個聯通塊進行合併的時候。對於聯通塊中的每個點都去\(trie\)上搜尋他能找到的最優秀的邊。也就是說如果當前位置是\(1\)那麼就搜尋1子樹,否則的話既要搜0子樹,也要搜1子樹。這樣1子樹是一定要搜的。所以把0子樹變為1子樹和0子樹合併起來的結果。然後就可以搜尋了。
還有乙個問題就是。如果當前子樹中的所有點都已經在這個聯通塊裡了怎麼辦。所以統計出每棵子樹中聯通塊編號的最大值和最小值。然後就可以知道當前子樹中是不是還有不屬於這個聯通塊裡的點了。合併聯通塊之後再把每個點合併的聯通塊裡的點所屬的聯通塊修改一下就行了。
複雜度\(o((n+2^m)mlogn)\)
上面的做法**長且思路複雜。有一種更好的做法。
先把所有權值相同的點連一條邊。這樣肯定會比較優秀。
然後考慮列舉最終答案中\(w[u]\&w[v]\)的值p。
因為\(x\&w[u] \leq w[u]\),倒著列舉p,然後找到乙個點u使得\(w[u]\&p=p\)。然後從其他的等於滿足\(w[v]\&p=p\)的點\(v\)中找乙個與\(u\)不在同乙個聯通塊裡的點。將這兩個點之間連邊。貢獻為\(p\)。
萬一\(w[u]\&w[v]\)比\(p\)大呢。可以證明這是不可能的。因為p是倒著列舉的,如果\(w[u]\&w[v]>p\)那麼肯定之前就連過邊了。不會再連一次。
複雜度\(o(2^mm\alpha(n))\)
/*
* @author: wxyww
* @date: 2019-01-21 15:46:58
* @last modified time: 2019-01-21 15:53:01
*/#include#include#include#include#include#includeusing namespace std;
typedef long long ll;
const int n = 1000000 +10;
ll read()
while(c>='0'&&c<='9')
return x*f;
}int p[n],fa[n];
ll ans;
int find(int x)
void uni(int x,int y)
int main()
int k = (1 << m);
for(int i = 1;i <= k;++i) fa[i] = i;
for(int i = (1 << m) - 1;i;--i) }}
cout
}
uoj 176 新年的繁榮
給出乙個完全圖,邊權為兩點權值的and,求最大生成樹。這題用最小生成樹的boruvka演算法。大概就是每次找到每乙個聯通塊權值最大的邊,將這些聯通塊合併,直到只剩乙個聯通塊。因為每次聯通塊的個數至少減半,所以只會做log次操作。那麼這題相當於每個點有不同顏色,要找到每個點的異色點中最大的and值。扔...
uoj176 新年的繁榮
對於這種邊權難以直接維護的都直接考慮brouvka演算法。顯然,我們要做的是實現乙個可以查詢 x最大的資料結構。可以先對於所有權值建立一顆01 trie樹。考慮在trie樹查詢答案的過程,可以考慮乙個從高位到低位的貪心。當x的第i位為1時,最優策略一定是能走1就走1。當x的第i位為0時,既可以走0也...
uoj 175 新年的網警
在這新年的第一天,猴族首領猴腮雷打算來整治一下網路風氣。這時,他聽說在乙個叫做 universal oj 使用者群 的 qq 群中有人在散播 開 謠言 車 於是他就派了一群網警把這個使用者群裡的人都抓了回來,試圖找到謠言的源頭。這個使用者群中有 nn 個人,這些人中存在 mm 對雙向的直接認識關係,...