處理給出無向圖,會出現重邊,共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中所有頂點都在同一連通分量上為...