洛谷 P4180 【模板】嚴格次小生成樹[BJWC2010]
阿新 • • 發佈:2018-12-13
(有任何問題歡迎留言或私聊 && 歡迎交流討論哦
Catalog
原題目描述在最下面。
意思很裸,就是求一個嚴格意義的次小生成樹,樹邊權和要小於MST邊權和。
Solution:
本來以為就普通的次小生成樹,然後取不等於MST的即可,結果樣例都過不了!
(ps: 如果你不會求普通最小生成樹,可能看不懂下面這段話,唉)
後來發現這樣是錯的,這種方法能保證求出非嚴格次小生成樹,僅此而已!其他權值都是無意義的。因為普通方法使用MST路徑上最大的邊權和它交換,若兩者邊權相等,這樣的答案是不滿足要求的,最重要的是你漏解了!
Kruskal+倍增:
這是一種比較好寫的正解。
為解決上面的問題,用倍增來維護MST上每個點往上的最大邊權mx1和嚴格次大邊權mx2。
列舉不在MST中的邊,求u,v的LCA, cf =LCA。再求出cf->u和cf->v上的不等於w的最大邊權。
若mx1 == w, 則用mx2交換w;反之用mx1交換w。
(u,v,w是不在生成樹中的邊的端點和權值)
具體細節看程式碼吧,不懂的可以評論區留言。
AC_Code:
/* 5 6 1 2 1 1 3 2 2 4 3 3 5 4 3 4 3 4 5 6 */ #include <cstdio> #include <cstring> #include <algorithm> #include <queue> #include <ctime> #include<iostream> using namespace std; typedef long long LL; const int MXN = 1e5 + 5; const int MXE = 6e5 + 5; const int INF = 0x3f3f3f3f; int n, m; LL dis[MXN], dis1[MXN][23], dis2[MXN][23]; int dep[MXN], up[MXN][23]; int FA[MXN], rk[MXN]; int head[MXN], tot; struct lp{ int v, w, nex; }cw[MXE]; struct EDGE{ int u, v, w, is; }edge[MXE]; bool cmp(const EDGE& a, const EDGE& b) { return a.w < b.w; } void init() { tot = -1; memset(up, 0, sizeof(up)); memset(head, -1, sizeof(head)); memset(dis, 0, sizeof(dis)); memset(dis1, -1, sizeof(dis1)); memset(dis2, 0, sizeof(dis2)); memset(dep, 0, sizeof(dep)); } void add(int u,int v,int w) { cw[++tot].v = v; cw[tot].nex = head[u]; cw[tot].w = w; head[u] = tot; cw[++tot].v = u; cw[tot].nex = head[v]; cw[tot].w = w; head[v] = tot; } void dfs(int u, int ba, int d) { dep[u] = d; up[u][0] = ba; for(int i = 1; i < 20; ++i) { int cf = up[u][i-1]; up[u][i] = up[cf][i-1]; dis1[u][i] = max(dis1[u][i-1], dis1[cf][i-1]); if(dis1[u][i-1] == dis1[cf][i-1]) dis2[u][i] = max(dis2[u][i-1], dis2[cf][i-1]); else { dis2[u][i] = min(dis1[u][i-1], dis1[cf][i-1]); dis2[u][i] = max(dis2[u][i], max(dis2[u][i-1], dis2[cf][i-1])); } } for(int i = head[u]; ~i; i = cw[i].nex) { int v = cw[i].v; if(v == ba) continue; dis[v] = dis[u] + cw[i].w; dis1[v][0] = cw[i].w; up[v][0] = u; dfs(v, u, d + 1); } } int LCA(int x, int y) { if(dep[x] < dep[y]) swap(x, y); for(int i = 19; i >= 0; --i) { if(dep[up[x][i]] >= dep[y]) { x = up[x][i]; } } if(x == y) return x; for(int i = 19; i >= 0; --i) { if(up[x][i] != up[y][i]) { x = up[x][i]; y = up[y][i]; } } return up[x][0]; } int Fi(int x) { return FA[x] == x? x: FA[x] = Fi(FA[x]); } LL calc(int u, int v,int w) { if(dep[u] < dep[v]) swap(u, v); int k = dep[u] - dep[v]; LL mx1 = 0, mx2 = 0; for(int i = 0; i < 20; ++i) { if((1<<i)&k) { if(dis1[u][i] > mx1) { mx2 = mx1; mx1 = dis1[u][i]; } mx2 = max(mx2, dis2[u][i]); u = up[u][i]; } } if(mx1 != w) return w - mx1; return w - mx2; } void solve() { scanf("%d%d", &n, &m); init(); for(int i = 0; i < m; ++i) { scanf("%d%d%d", &edge[i].u, &edge[i].v, &edge[i].w); edge[i].is = 0; } for(int i = 1; i <= n; ++i) FA[i] = i, rk[i] = 1; sort(edge, edge + m, cmp); int cnt = 0; LL MAX = 0; for(int i = 0; i < m; ++i) { int pa = Fi(edge[i].u), pb = Fi(edge[i].v); if(pa == pb) continue; if(rk[pa] > rk[pb]) swap(pa,pb); FA[pa] = pb; rk[pb] += rk[pa]; add(edge[i].u, edge[i].v,edge[i].w); MAX += edge[i].w; cnt ++; edge[i].is = 1; if(cnt == n-1) break; } //printf("*** %d\n", MAX); up[1][0] = 1; dis1[1][0] = 0; dfs(1, 1, 1); LL tmp = 1e18; for(int i = 0; i < m; ++i) { if(edge[i].is) continue; int cf = LCA(edge[i].u,edge[i].v); tmp = min(tmp, calc(edge[i].u,cf,edge[i].w)); tmp = min(tmp, calc(edge[i].v,cf,edge[i].w)); //printf("tmp = %d\n", tmp); } printf("%lld\n", MAX + tmp); } int main(int argc, char const *argv[]){ solve(); return 0; }