bzoj1977: [BeiJing2010組隊]次小生成樹 Tree(嚴格次小生成樹 樹鏈剖分+線段樹)
1977: [BeiJing2010組隊]次小生成樹 Tree
Time Limit: 10 Sec Memory Limit: 512 MB
Submit: 4005 Solved: 1161
[Submit][Status][Discuss]
Description
小 C 最近學了很多最小生成樹的演算法,Prim 演算法、Kurskal 演算法、消圈演算法等等。 正當小 C 洋洋得意之時,小 P 又來潑小 C 冷水了。小 P 說,讓小 C 求出一個無向圖的次小生成樹,而且這個次小生成樹還得是嚴格次小的,也就是說: 如果最小生成樹選擇的邊集是 EM,嚴格次小生成樹選擇的邊集是 ES,那麼需要滿足:(value(e) 表示邊 e的權值)
Input
第一行包含兩個整數N 和M,表示無向圖的點數與邊數。 接下來 M行,每行 3個數x y z 表示,點 x 和點y之間有一條邊,邊的權值為z。
Output
包含一行,僅一個數,表示嚴格次小生成樹的邊權和。(資料保證必定存在嚴格次小生成樹)
Sample Input
5 6
1 2 1
1 3 2
2 4 3
3 5 4
3 4 3
4 5 6
Sample Output
11
HINT
資料中無向圖無自環; 50% 的資料N≤2 000 M≤3 000; 80% 的資料N≤50 000 M≤100 000; 100% 的資料N≤100 000 M≤300 000 ,邊權值非負且不超過 10^9 。
Source
一個明顯的定義:
次小生成樹一定是最小生成樹中的某一條邊“走錯了”的產物
由此衍生出一個構造法:列舉剩餘的m - n + 1條邊, E(u, v),在最小生成樹上的u, v的鏈上刪去最大的那條邊,加入新邊
所有這樣構造的樹中最小的就是次小生成樹
但是本題是嚴格次小,所以還要維護嚴格次大邊
#include <bits/stdc++.h> using namespace std; const int MAXN = 100100; const int MAXE = 300300; const int INF = 0x3f3f3f3f; template <typename T> inline void read(T &x) { int c = getchar(); bool f = false; for (x = 0; !isdigit(c); c = getchar()) { if (c == '-') { f = true; } } for (; isdigit(c); c = getchar()) { x = x * 10 + c - '0'; } if (f) { x = -x; } } struct edge { int u, v; int len; edge(int _u = 0, int _v = 0, int _l = 0) : u(_u), v(_v), len(_l) {} }e[MAXE], e2[MAXE]; int n, m, tot, tote; bool operator < (edge a, edge b) { return a.len < b.len; } int Fa[MAXN], rnk[MAXN], cnt; int Find(int x) { return x == Fa[x] ? x : Fa[x] = Find(Fa[x]); } void unite(int x, int y) { int f1 = Find(x), f2 = Find(y); if(rnk[f1] < rnk[f2]) Fa[f1] = f2; else { Fa[f2] = f1; if(rnk[f1] == rnk[y]) rnk[f1]++; } } struct Edge { int to, nxt, len; Edge() {} Edge(int _to, int _nxt, int _len) : to(_to), nxt(_nxt), len(_len){} }E[MAXN << 1]; int h[MAXN], Lr[MAXN], Rr[MAXN], val[MAXN]; int sz[MAXN], son[MAXN], fa[MAXN], dep[MAXN], top[MAXN]; inline void add_edge(int u, int v, int w) { E[++cnt] = Edge(v, h[u], w), h[u] = cnt; E[++cnt] = Edge(u, h[v], w), h[v] = cnt; } void dfs1(int x) { sz[x] = 1; dep[x] = dep[fa[x]] + 1; for(int i = h[x]; i; i = E[i].nxt) { int to = E[i].to; if(to == fa[x]) continue; fa[to] = x; val[to] = E[i].len; dfs1(to); sz[x] += sz[to]; if(sz[to] > sz[son[x]]) son[x] = to; } } void dfs2(int x) { Lr[x] = ++tot; Rr[Lr[x]] = val[x]; if(x == son[fa[x]]) top[x] = top[fa[x]]; else top[x] = x; if(son[x]) dfs2(son[x]); for(int i = h[x]; i; i = E[i].nxt) { int to = E[i].to; if(to == fa[x] || to == son[x]) continue; dfs2(to); } } struct SegmentTree { int l, r; int val; SegmentTree() { l = r = 0; val = -INF; } }tre[MAXN << 2]; #define Ls(x) x << 1 #define Rs(x) x << 1 | 1 void push_up(int x) { tre[x].val = max(tre[Ls(x)].val, tre[Rs(x)].val); } void build(int x, int l, int r) { tre[x].l = l, tre[x].r = r; if(l == r) { tre[x].val = Rr[l]; return ; } int mid = (l + r) >> 1; build(Ls(x), l, mid); build(Rs(x), mid + 1, r); push_up(x); } typedef pair<int, int> pii; #define mp(a, b) make_pair(a, b) #define X first #define Y second pair<int, int> query(int x, int L, int R) { int l = tre[x].l, r = tre[x].r; if(L <= l && R >= r) return make_pair(tre[x].val, -INF); if(L > r || l > R) return make_pair(-INF, -INF - 1); int mid = (l + r) >> 1; pair<int, int> ret = make_pair(-INF, -INF - 1); if(mid >= L) { pii tmp = query(Ls(x), L, R); if(tmp.X > ret.X) ret.Y = ret.X, ret.X = tmp.X; if(tmp.Y > ret.Y && tmp.Y != ret.X) ret.Y = tmp.Y; if(tmp.X < ret.X && tmp.X > ret.Y) ret.Y = tmp.X; } if(mid < R) { pii tmp = query(Rs(x), L, R); if(tmp.X > ret.X) ret.Y = ret.X, ret.X = tmp.X; if(tmp.Y > ret.Y && tmp.Y != ret.X) ret.Y = tmp.Y; if(tmp.X < ret.X && tmp.X > ret.Y) ret.Y = tmp.X; } return ret; } pii qmax(int u, int v) { int f1 = top[u], f2 = top[v]; pii ans = mp(-INF, -INF); while(f1 != f2) { if(dep[f1] < dep[f2]) { swap(f1, f2); swap(u, v); } pii tmp = query(1, Lr[f1], Lr[u]); if(tmp.X > ans.X) ans.Y = ans.X, ans.X = tmp.X; if(tmp.Y > ans.Y && tmp.Y != ans.X) ans.Y = tmp.Y; if(tmp.X < ans.X && tmp.X > ans.Y) ans.Y = tmp.X; u = fa[f1]; f1 = top[u]; } if(u == v) return ans; if(dep[u] > dep[v]) swap(u, v); pii tmp = query(1, Lr[son[u]], Lr[v]); if(tmp.X > ans.X) ans.Y = ans.X, ans.X = tmp.X; if(tmp.Y > ans.Y && tmp.Y != ans.X) ans.Y = tmp.Y; if(tmp.X < ans.X && tmp.X > ans.Y) ans.Y = tmp.X; return ans; } #define LL long long signed main() { read(n); read(m); for(int i = 1; i <= m; i++) { int a, b, c; read(a), read(b), read(c); e[i] = edge(a, b, c); } sort(e + 1, e + m + 1); long long sum = 0; for(int i = 1; i <= n; i++) Fa[i] = i; for(int i = 1; i <= m; i++) { int u = e[i].u; int v = e[i].v; int len = e[i].len; if(Find(u) != Find(v)) { add_edge(u, v, len); unite(u, v); sum += (LL) len; } else { e2[++tote] = edge(u, v, len); } } dfs1(1), dfs2(1); build(1, 1, n); LL ans = INF; ans *= 1000000; for(int i = 1; i <= tote; i++) { int u = e2[i].u; int v = e2[i].v; int len = e2[i].len; pii lm = qmax(u, v); if(sum < sum - (LL)lm.X + (LL)len) ans = min(ans, sum - (LL)lm.X + (LL)len); else ans = min(ans, sum - (LL)lm.Y + (LL)len); } printf("%lld\n", ans); return 0; }
相關推薦
bzoj1977: [BeiJing2010組隊]次小生成樹 Tree(嚴格次小生成樹 樹鏈剖分+線段樹)
1977: [BeiJing2010組隊]次小生成樹 Tree Time Limit: 10 Sec Memory Limit: 512 MB Submit: 4005 Solved: 1161 [Submit][Status][Discuss] Descriptio
【bzoj2238】Mst 最小生成樹+樹鏈剖分+線段樹
生成樹 brush 輸出 兩個 下一條 整數 algorithm ted sin 題目描述 給出一個N個點M條邊的無向帶權圖,以及Q個詢問,每次詢問在圖中刪掉一條邊後圖的最小生成樹。(各詢問間獨立,每次詢問不對之後的詢問產生影響,即被刪掉的邊在下一條詢問中依然存在) 輸
【Codeforces827D/CF827D】Best Edge Weight(最小生成樹性質+倍增/樹鏈剖分+線段樹)
題目 分析 倍增神題……(感謝T*C神犇給我講qwq) 這道題需要考慮最小生成樹的性質。首先隨便求出一棵最小生成樹,把樹邊和非樹邊分開處理。 首先,對於非樹邊(u,v)(u,v)(u,v)(表示一條兩端點為uuu和vvv的邊,下同)。考慮Kruskal演算法的
Query on a tree 【SPOJ - QTREE】【樹鏈剖分+線段樹區間最大值】
題目連結 一道樹鏈剖分的基礎題,我的做法可能與廣大網友不大一樣,我將邊也算做是一個點,然後相當於是把兩個端點相互連線在邊所代表的那個點上,這樣更新邊就變成了更新點,查詢就是區間查詢。 一開始讀了道假題做了半天(最近總是在讀假題……),還以為是區間和,然後看
[CF916E]Jamie and Tree——樹鏈剖分+線段樹
題目大意: 有一棵n個點的樹,每個節點上有一個權值wi,最開始根為1號點.現在有3種 型別的操作: • 1 root, 表示將根設為root. • 2 u v x, 設u, v的最近公共祖先為p, 將p的子樹中的所有點的權值加上x. • 3 u, 查詢
校內賽 codeforces 827D【最小生成樹】【樹鏈剖分】 解題報告
找不到題面!! 題意 給出一張n(<=2e5)個點 m(<=2e5)條邊無向圖,保證有生成樹。對於每條邊,給出一個最大值maxLength,咦即能夠保證這條邊能夠出現在所有的最小生成樹中,邊權的最大值為maxLength(同時,其他所有邊長度不變
Codeforces 827D Best Edge Weight (最小生成樹 + 樹鏈剖分/倍增/並查集)
Codeforces 827D Best Edge Weight 題意: 給你N個點M條邊的帶邊權無向聯通圖,現在對於每條邊,問這條邊的權值最大可以是多 少,使得這條邊在該無向圖的所有最最小成樹中? 資料範圍 2 ≤N ≤ 2*1055, N -
【LSGDOJ1834 Tree】樹鏈剖分
done using 給定 continue 返回 ace 最大的 接下來 chan 題目描述 給定一個N個結點的無向樹,樹中的結點按照1...N編號,樹中的邊按照1...N ? 1編號,每條邊都賦予一個權值。你需要編寫程序支持以下三種操作: 1. CHANGE i
CSU 1663: Tree(樹鏈剖分)
sample tree sub enter hup eof == contains som 1663: Tree Time Limit: 5 Sec Memory Limit: 128 MB Submit: 26 Solved: 11 [Submi
HDU 5044 Tree(樹鏈剖分)
int str ans hang line sin _id rgb php HDU 5044 Tree field=problem&key=2014+ACM%2FICPC+Asia+Regional+Shanghai
hdu 5044 Tree (樹鏈剖分+標記數組)
chan main while class #define AR spa def ble 鏈接:http://acm.hdu.edu.cn/showproblem.php?pid=5044 這道題是真的有毒,之前用樹鏈剖分+線段樹寫,tle了一萬發,瘋狂優化,最後放棄了,
樹鏈剖分【p4116】Qtree3 - Query on a tree
Description 給出N個點的一棵樹(N-1條邊),節點有白有黑,初始全為白 有兩種操作: 0 i : 改變某點的顏色(原來是黑的變白,原來是白的變黑) 1 v : 詢問1到v的路徑上的第一個黑點,若無,輸出-1 Input 第一行 N,Q,表示N個點和Q個操作 第二行
樹鏈剖分【CF343D】Water Tree
Description Mad scientist Mike has constructed a rooted tree, which consists of nnvertices. Each vertex is a reservoir which can be either empty or fi
CF 504E Misha and LCP on Tree——字尾陣列+樹鏈剖分
題目:http://codeforces.com/contest/504/problem/E 樹鏈剖分,把重鏈都接起來,且把每條重鏈的另一種方向的也都接上,在這個 2*n 的序列上跑字尾陣列。 對於詢問,把兩條鏈拆成一些重鏈的片段,然後兩個指標列舉每個片段,用字尾陣列找片段與片段的 LCP ,直到一次 L
CF504E Misha and LCP on Tree(樹鏈剖分+後綴樹組)
scan getchar() sca 我們 。。 i++ top sin num 1A真舒服。 喜聞樂見的樹鏈剖分+SA。 一個初步的想法就是用樹鏈剖分,把兩個字符串求出然後hash+二分lcp。。。不存在的。 我們用樹鏈剖分拼出兩個字符串(用樹剖拼出的這兩個字符串,一定是
Tree 【POJ - 3237】【樹鏈剖分+一些特殊的處理】
題目連結 這道題,說來還的確困擾了我一個多小時,當時就在想,我該如何處理那些邊權(我將邊化為點)以及點(預設權值為0)的取相反數後的處理(因為點取相反數之後還是0),會困擾到那些邊的。 然後,我想到了,如果這段區間的返回的值為0,那麼就說明了肯定是點的,我
SPOJ375——Query on a tree(樹鏈剖分模板詳解以及入門)
You are given a tree (an acyclic undirected connected graph) with N nodes, and edges numbered 1, 2, 3...N-1. We will ask you to perfro
CF1111E Tree 樹鏈剖分,DP
return eof == 一個 發現 ++ query tree def CF1111E Tree 過年了,洛咕還沒爬這次的題,先放個CF的鏈接吧。 對於每個詢問點\(x\),設它的祖先即不能和它放在同一個集合中的點的個數為\(f[x]\),設\(dp[i][j]\)表示
AGC 014 E Blue and Red Tree [樹鏈剖分]
std namespace emp open http rgs back 可能 www. 傳送門 思路 官方題解是倒推,這裏提供一種正推的做法。 不知道你們是怎麽想到倒推的……感覺正推更好想啊QwQ就是不好碼 把每一條紅邊,將其轉化為藍樹上的一條路徑。為了連這條紅邊,需要
SPOJ 375 Query on a tree(初學樹鏈剖分)
You are given a tree (an acyclic undirected connected graph) with N nodes, and edges numbered 1, 2, 3...N-1. We will ask you to perfrom some instructions