日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

支配树算法

發布時間:2023/12/20 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 支配树算法 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

簡介

在一個有向圖里,可以有環也可以無環,當一個點u到另一個點v的所有可行路徑都必經一個點w,那么可以稱u被w支配,所以一個點必定會被一個點支配(除了頂點)
支配樹是一個單源算法(學習支配樹的時候因為不知道這個被坑了4天,感謝某位大佬抬了一手)

樹形圖

因為該圖本身就是樹狀,所以這顆樹本身就是自己的支配樹,顯然根節點到任意非根節點都需要經過樹路徑上的所有點,這些點都是其支配點。

DAG圖下建支配樹

由于DAG圖是有向無環圖,所以只需要進行拓撲序建樹即可,可以快速得到支配樹的上層結構。
例如得到了1-n每個點的拓撲序,也得到了拓撲排序后的點順序,然后按照拓撲序進行遍歷,但遍歷到x點的時候,1-(x-1)點的結構已經建立完了,然后通過遍歷他的所有父親節點,得到其中的LCA,然后將他自己連到LCA上,由于是一邊建樹一邊查詢LCA,所以用到倍增LCA進行動態LCA。
由于是有向圖,所以可以同時存其反向圖,遍歷反向圖進行拓撲排序,正向圖進行建樹。
Graph參見 Graph圖結構體

const int MAX_DEP = 20;// 注意0,1點的邊界問題 struct DominatorTree {int deg[N]; // 入度int dep[N]; //int dfn[N];int st[N];int tot;// 拓撲序, 要保證root是入度為0void bfs(Graph &gh, int root) {queue<int> q;q.push(root);tot = 0;while (!q.empty()) {int u = q.front();q.pop();dfn[u] = ++tot;st[tot] = u;forg(i, gh.head[u], gh.eg) {int v = gh.eg[i].e;if ((--deg[v]) == 0) {q.push(v);}}}}// 倍增2^k的父親int fa[N][MAX_DEP];// 倍增LCAint lca(int u, int v) {if (dep[u] > dep[v]) {swap(u, v);}int hu = dep[u], hv = dep[v];int tu = u, tv = v;for (int det = hv - hu, i = 0; det; det >>= 1, i++) {if (det & 1)tv = fa[tv][i];}if (tu == tv) {return tu;}for (int i = MAX_DEP - 1; i >= 0; i--) {if (fa[tu][i] == fa[tv][i]) {continue;}tu = fa[tu][i];tv = fa[tv][i];}return fa[tu][0];}// 動態更新節點的父親屬性void lineFa(int u, int v) {fa[u][0] = v;for (int i = 1; i < MAX_DEP; i++) {v = fa[u][i] = fa[v][i - 1];}}// 建樹, op是gh的反向圖,用來尋找其父親void build(Graph &gh, Graph &op, int n, int root) {memcpy(deg, gh.deg, sizeof(int) * (n + 1));bfs(gh, root);for (int k = 1; k <= tot; k++) {int u = st[k], fath = -1;dep[u] = 0;for (int i = op.head[u]; ~i; i = op.eg[i].nxt) {int v = op.eg[i].e;if (dfn[v] > dfn[u]) continue;fath = (fath == -1 ? v : lca(fath, v));}if (fath == -1) fath = u;lineFa(u, fath);dep[u] = dep[fath] + 1;}} } dtree;

有向圖

Lengauer-Tarjan算法(待學)

例題

再貼一個被折磨了4天的那道題(順便學了下倍增LCA),HDU杭電2019暑期多校第三場B題
一個DAG圖下每次詢問給出的兩個點u,v到所有出度為0的點的支配點個數
這題通過反向建邊+虛點連接所有出度為0的點建立支配樹,每次查詢的時候,查詢樹上的u,v兩點的深度,容斥減去其LCA點的深度,就是其支配點的個數
HDU 6604 Blow up the city

// 巨菜的ACMer-Happy233#include <bits/stdc++.h>using namespace std;//----- typedef double db; typedef long long ll; typedef unsigned int ui; typedef vector<int> vi; typedef pair<int, int> pii; typedef pair<ll, ll> pll; #define mp make_pair #define fi first #define se second #define pw(x) (1ll << (x)) #define bt(x, i) ((x >> i) & 1) #define sz(x) ((int)(x).size()) #define all(x) (x).begin(),(x).end() #define rep(i, l, r) for(int i=(l);i<(r);++i) #define per(i, l, r) for(int i=(r)-1;i>=(l);--i) #define sf(x) scanf("%d", &(x)) #ifndef ACM_LOCAL #define endl '\n' #endifconst double pi = acos(-1); const int MOD = int(998244353);#define forg(i, h, eg) for(int i = (h); ~i; i = (eg[i]).nxt)struct Edge {int e, nxt;ll v;Edge() = default;Edge(int a, ll b, int c = 0) : e(a), v(b), nxt(c) {}bool operator<(const Edge &a) const {return (a.v == v ? e < a.e : v < a.v);} };const ll INF = ll(1e11); const int N = int(1e5 + 10); const int M = int(3e5 + 10);struct Graph {Edge eg[M];int deg[N];int head[N];int cnt;void init(int n) {memset(head, -1, sizeof(int) * ++n);memset(deg, 0, sizeof(int) * n);cnt = 0;}inline void addEdge(int x, int y, ll v = 0) {eg[cnt] = Edge(y, v, head[x]);head[x] = cnt++;deg[y]++;} };const int MAX_DEP = 20;struct DominatorTree {int deg[N]; // 入度int dep[N]; //int dfn[N];int st[N];int tot;// 拓撲序, 要保證root是入度為0void bfs(Graph &gh, int root) {queue<int> q;q.push(root);tot = 0;while (!q.empty()) {int u = q.front();q.pop();dfn[u] = ++tot;st[tot] = u;forg(i, gh.head[u], gh.eg) {int v = gh.eg[i].e;if ((--deg[v]) == 0) {q.push(v);}}}}// 倍增2^k的父親int fa[N][MAX_DEP];// 倍增LCAint lca(int u, int v) {if (dep[u] > dep[v]) {swap(u, v);}int hu = dep[u], hv = dep[v];int tu = u, tv = v;for (int det = hv - hu, i = 0; det; det >>= 1, i++) {if (det & 1)tv = fa[tv][i];}if (tu == tv) {return tu;}for (int i = MAX_DEP - 1; i >= 0; i--) {if (fa[tu][i] == fa[tv][i]) {continue;}tu = fa[tu][i];tv = fa[tv][i];}return fa[tu][0];}void lineFa(int u, int v) {fa[u][0] = v;for (int i = 1; i < MAX_DEP; i++) {v = fa[u][i] = fa[v][i - 1];}}void build(Graph &gh, Graph &op, int n, int root) {memcpy(deg, gh.deg, sizeof(int) * (n + 1));bfs(gh, root);for (int k = 1; k <= tot; k++) {int u = st[k], fath = -1;dep[u] = 0;for (int i = op.head[u]; ~i; i = op.eg[i].nxt) {int v = op.eg[i].e;if (dfn[v] > dfn[u]) continue;fath = (fath == -1 ? v : lca(fath, v));}if (fath == -1) fath = u;lineFa(u, fath);dep[u] = dep[fath] + 1;}} } dtree;Graph gh, op;void solve() {int n, m;while (cin >> n >> m) {gh.init(n);op.init(n);rep(i, 0, m) {int a, b;cin >> a >> b;gh.addEdge(b, a);op.addEdge(a, b);}for (int i = 1; i <= n; i++) {if (gh.deg[i] == 0) {gh.addEdge(0, i);op.addEdge(i, 0);}}dtree.build(gh, op, n, 0);int q;cin >> q;while (q--) {int u, v;cin >> u >> v;int f = dtree.lca(u, v);int ans = dtree.dep[u] + dtree.dep[v] - dtree.dep[f] - 1;cout << ans << endl;}} }int main() { #ifdef ACM_LOCALfreopen("./data/1.in", "r", stdin);// freopen("./data/std.out", "w", stdout); #elseios_base::sync_with_stdio(false);cin.tie(0);cout.tie(0); #endif#ifdef ACM_LOCALauto start = clock(); #endifint t;cin >> t;while (t--)solve(); #ifdef ACM_LOCALauto end = clock();cerr << "Run Time: " << double(end - start) / CLOCKS_PER_SEC << "s" << endl; #endifreturn 0; }

總結

以上是生活随笔為你收集整理的支配树算法的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。