hdu 6200 mustedge mustedge mustedge(樹鏈剖分 + 樹狀陣列 + 並差集)
阿新 • • 發佈:2020-09-19
題意:
開始給你一個n點,m邊的無向聯通圖, 之後有q個詢問,1操作是將u,v連邊,2操作是詢問u,v之間的關鍵路徑(就是u到v無論走那條路都必須經過的邊)
解法:
一個無向聯通圖找關鍵路徑首先想到支配樹,但要動態維護,所以必不可能。再想想如果兩點在同一個環上的話,那不就環上的所有的邊都不可能是關鍵路徑了嗎,那直接將這些邊的權值消掉就好
所以我們直接建樹,如果有多的邊,可以先存起來,在q個詢問前當操作1先用掉。
對於操作1,所加的邊一定會成環,所以我們將樹上屬於這個環的的鏈權值清零
這裡我們先將樹樹鏈剖分,放到樹狀陣列上,在這點對應的dfn[x]的值+1,在dfn[x]+x子數大小這個位置-1, 這樣樹狀陣列就能回答從根節點到x的被消掉的關鍵路徑的個數
操作2可以直接利用樹狀陣列和dep來獲得答案
程式碼:
#include <bits/stdc++.h> using namespace std; typedef long long ll; const ll N = 2e5 + 7; int t; int n, m; int u, v, op, q; const ll MAXN = 2e5 + 7; int c[MAXN]; int lowbit(int x){ return x & (-x);} void add(int x, int y, int n) { // 下標為x 的數 增加 y (相對修改) for (int i = x; i <= n; i += lowbit(i)) c[i] += y; } int getsum(int x) { // 獲取 1-x 的字首和 int ans = 0; for (int i = x; i; i -= lowbit(i)) ans += c[i]; return ans; } int f[N]; int fa[N], dep[N], head[N], siz[N], son[N]; int top[N], dfn[N], dfnr[N]; int idx, cnte = 0; struct Edge{ int to, nxt; } e[N << 1]; vector<int> vv; void addedge(int u,int v) { e[++cnte].to = v; e[cnte].nxt = head[u]; head[u] = cnte; } void dfs1(int u, int far) { fa[u] = far; dep[u] = dep[far] + 1; siz[u] = 1; dfn[u] = ++idx; son[u] = 0; for (int i = head[u]; i;i = e[i].nxt) { int to = e[i].to; if(to != far) { dfs1(to, u); siz[u] += siz[to]; if(son[u] == 0 || siz[son[u]] < siz[to]) son[u] = to; } } dfnr[u] = idx; } void dfs2(int u, int tfar) { top[u] = tfar; if(son[u]) dfs2(son[u], tfar); for (int i = head[u]; i;i = e[i].nxt) { if(e[i].to != son[u] && e[i].to != fa[u]) dfs2(e[i].to, e[i].to); } } int find(int x) { if(x != f[x]) return f[x] = find(f[x]); return x; } void unio(int x, int y) { int fx = find(x); int fy = find(y); if(fx != fy) f[fx] = fy; } int LCA(int x, int y) { while(top[x] != top[y]) { dep[top[x]] > dep[top[y]] ? x = fa[top[x]] : y = fa[top[y]]; } return dep[x] < dep[y] ? x : y; } void merge(int x, int y) { int fx = find(x), fy = find(y); if(fx == fy) return; int lca = LCA(x, y); while(find(x) != find(lca)) { fx = find(x); add(dfn[fx], 1, idx); add(dfn[fx] + siz[fx], -1, idx); unio(fx, fa[fx]); } while(find(y) != find(lca)) { fy = find(y); add(dfn[fy], 1, idx); add(dfn[fy] + siz[fy], -1, idx); unio(fy, fa[fy]); } } void init() { cnte = 0; idx = 0; memset(c, 0, sizeof(c)); for (int i = 1; i <= n;i++) f[i] = i, head[i] = 0; vv.clear(); } int main() { scanf("%d", &t); for (int cas = 1; cas <= t;cas++) { scanf("%d %d", &n, &m); init(); for (int i = 1; i <= m; i++) { scanf("%d %d", &u, &v); if(find(u) != find(v)) { unio(u, v); addedge(u, v); addedge(v, u); } else { vv.push_back(u); vv.push_back(v); } } dfs1(1, 0); dfs2(1, 1); for (int i = 1; i <= n;i++ ) f[i] = i; for (int i = 0; i < vv.size(); i += 2) merge(vv[i], vv[i + 1]); scanf("%d", &q); printf("Case #%d:\n", cas); while(q--) { scanf("%d %d %d", &op, &u, &v); if(op == 1) { merge(u, v); } else { int lca = LCA(u, v); ll ans = (dep[u] + dep[v] - dep[lca] * 2) - (getsum(dfn[u]) + getsum(dfn[v]) - 2 * getsum(dfn[lca])) ; printf("%lld\n", ans); } } } return 0; }