克魯斯卡爾重構樹

2021-09-27 09:49:24 字數 1876 閱讀 6827

處理給出無向圖,會出現重邊,共m條路徑,每條路徑有乙個困難值,q次詢問,求從點x出發只經過困難值小於等於v的路徑,求某個值。

克魯斯卡爾重構樹的核心思想是,當新增最小生成樹的邊的時候,不在兩個點之間直接加邊,而是新建節點,讓邊的兩個節點分別成為它的左右兒子節點,然後這個新建的點,就成為整個聯通塊的代表點,點權為連邊的值(最開始的n個點為點權)。

性質:乙個點的所有子樹節點的權值都小於等於它的權值,並且從它開始逐漸向子節點移動,權值是單調不上公升的。查詢時,可以樹上倍增得到當前查詢點能夠到達的最遠的祖先點,那麼從這個點能夠到達的符合邊權限制條件的聯通塊的節點,就是祖先節點的子樹中所有的葉節點。

例題:

生成克魯斯卡爾重構樹後跑出dfs序,根據dfs序建立線段樹計算區間點權之積

#include using namespace std;

#define lson rt << 1, l, mid

#define rson rt << 1 | 1, mid + 1, r

typedef long long ll;

const ll mod = 998244353;

const int maxn = 1e5 + 5;

int n, m, q;

ll a[maxn << 1];

struct edge

} edge[maxn << 1];

int p[maxn << 1];

int value[maxn << 1], cnt;

int find(int x)

vectorload[maxn << 1];

int depth[maxn << 1], grand[maxn << 1][20], n;

int in[maxn << 1], out[maxn << 1], ranks[maxn << 1], t;

void dfs(int s, int pre)

out[s] = t;

}void kruskal()

n = log2(cnt);

dfs(cnt, 0);

}ll mul[maxn << 3];

void build(int rt, int l, int r)

int mid = (l + r) >> 1;

build(lson), build(rson);

mul[rt] = mul[rt << 1] * mul[rt << 1 | 1] % mod;

}void update(int rt, int l, int r, int pos, int x)

int mid = (l + r) >> 1;

if (pos <= mid)

update(lson, pos, x);

else

update(rson, pos, x);

mul[rt] = mul[rt << 1] * mul[rt << 1 | 1] % mod;

}ll query(int rt, int l, int r, int l, int r)

int main(int argc, char const *ar**)

for (int i = 1; i <= m; ++i)

kruskal();

build(1, 1, cnt);

int op, x, y;

while (q--)

printf("%lld\n", query(1, 1, cnt, in[x], out[x]) % mod);

}} else

update(1, 1, cnt, in[x], y % mod);

}}

克魯斯卡爾重構樹

克魯斯卡爾重構樹就是在用克魯斯卡爾演算法計算最小生成樹時,構造出一棵樹。當題目需要用到最小生成樹時,常常利用重構樹將其轉化為樹上問題。重構過程 在連線兩個點時,將其連向乙個新點,新點的點權為這條邊的邊權。並查集上就把這兩點的父親設為這個新點。這樣我們就維護了一棵從底向上點權遞增的一棵二叉樹。這棵樹具...

克魯斯卡爾演算法

測試輸入包含若干測試用例。每個測試用例的第1行給出評估的道路條數 n 村莊數目m 100 隨後的 n 行對應村莊間道路的成本,每行給出一對正整數,分別是兩個村莊的編號,以及此兩村莊間道路的成本 也是正整數 為簡單起見,村莊從1到m編號。當n為0時,全部輸入結束,相應的結果不要輸出。對每個測試用例,在...

克魯斯卡爾演算法

設n v,是連通網 1 令最小生成樹的初始狀態為只有n個頂點而無邊的非連通圖t v,圖中每個頂點自成乙個連通分量 2 在e中選擇代價最小的邊,若該邊依附的頂點落在t中不同的連通分量上,則將此邊加入到t中,否則捨去此邊而選擇下一條代價最小的邊 3 反覆執行第2 步,直至t中所有頂點都在同一連通分量上為...