1. 程式人生 > >bzoj1977: [BeiJing2010組隊]次小生成樹 Tree(嚴格次小生成樹 樹鏈剖分+線段樹)

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的權值) 

 這下小 C 蒙了,他找到了你,希望你幫他解決這個問題。

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