1. 程式人生 > 實用技巧 >BZOJ #2238. Mst (最小生成樹+樹鏈剖分+線段樹)

BZOJ #2238. Mst (最小生成樹+樹鏈剖分+線段樹)

BZOJ #2238. Mst (最小生成樹+樹鏈剖分+線段樹)

Description

給出一個N個點M條邊的無向帶權圖,以及Q個詢問,每次詢問在圖中刪掉一條邊後圖的最小生成樹。(各詢問間獨立,每次詢問不對之後的詢問產生影響,即被刪掉的邊在下一條詢問中依然存在)

Input

第一行兩個正整數N,M(N<=50000,M<=100000)表示原圖的頂點數和邊數。

下面M行,每行三個整數X,Y,W描述了圖的一條邊(X,Y),其邊權為W(W<=10000)。保證兩點之間至多隻有一條邊。

接著一行一個正整數Q,表示詢問數。(1<=Q<=100000)

下面Q行,每行一個詢問,詢問中包含一個正整數T,表示把編號為T的邊刪掉(邊從1到M按輸入順序編號)。

思路:

我們可以首先對圖跑一個最小生成樹演算法得到圖的MST,

如果無法的得出生成樹,那麼直接特判接下來每一個輸出都是無解。

如果有生成樹,那麼對於所有非生成樹上的邊\((v,u)\)

它可以代替最小生成樹上\(v->u\) 這個路徑上所有邊

那麼我們可以在建立MST之後,進行樹鏈剖分,同時建立線段樹,功能需要有:

可以區間修改最小值,區間查詢最小值。

然後對於每一個非樹邊,我們可以區間更新路徑區間中的最小值,(注意這裡需要一個邊權轉點權的操作)。

那麼對於每一個邊被刪除之後的MST,分為兩種情況:

1、該邊是非樹邊,那麼答案還是最初的MST。

2、該邊是樹邊,那麼該邊應該替換為非樹邊構成的路徑覆蓋掉該邊的所有非樹邊

中權值最小的那個。顯然我們可以直接通過維護的線段樹得到那個最小權值,如果權值為無窮大(建立線段樹的初始值),那麼就代表沒有非樹邊可以cover該樹邊,答案為無解。

// 本題有一個巨坑的地方,題面說保證兩點之間至多隻有一條邊,但是資料中卻有這樣的情況,本人用assert親測。

程式碼:

#include <bits/stdc++.h>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <vector>
#include <iomanip>
#define ALL(x) (x).begin(), (x).end()
#define sz(a) int(a.size())
#define all(a) a.begin(), a.end()
#define rep(i,x,n) for(int i=x;i<n;i++)
#define repd(i,x,n) for(int i=x;i<=n;i++)
#define pii pair<int,int>
#define pll pair<long long ,long long>
#define gbtb ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define MS0(X) memset((X), 0, sizeof((X)))
#define MSC0(X) memset((X), '\0', sizeof((X)))
#define pb push_back
#define mp make_pair
#define fi first
#define se second
#define eps 1e-6
#define chu(x) cout<<"["<<#x<<" "<<(x)<<"]"<<endl
using namespace std;
typedef long long ll;
ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a;}
ll lcm(ll a, ll b) {return a / gcd(a, b) * b;}
ll powmod(ll a, ll b, ll MOD) {ll ans = 1; while (b) {if (b % 2)ans = ans * a % MOD; a = a * a % MOD; b /= 2;} return ans;}
inline void getInt(int* p);
const int maxn = 100000 + 10;
const int inf = 0x3f3f3f3f;
/*** TEMPLATE CODE * * STARTS HERE ***/
int n, m;
int root;
int cnt;// 編號用的變數
int top[maxn];// 所在重鏈的頂點編號
int id[maxn];//節點的新編號。
typedef pair<int, ll> pil;
std::vector<pil> son[maxn];
int SZ[maxn];// 子數大小
int wson[maxn];// 重兒子
int fa[maxn];// 父節點
int dep[maxn];// 節點的深度
void dfs1(int id, int pre, int step) // 維護出sz,wson,fa,dep
{
    dep[id] = step;
    fa[id] = pre;
    SZ[id] = 1;
    int  maxson = -1;
    for (auto x : son[id])
    {
        if (x.fi != pre)
        {
            dfs1(x.fi, id, step + 1);
            SZ[id] += SZ[x.fi];
            if (SZ[x.fi] > maxson)
            {
                maxson = SZ[x.fi];
                wson[id] = x.fi;
            }
        }
    }
}

//處理出top[],wt[],id[]
void dfs2(int u, int topf)
{
    id[u] = ++cnt;
    top[u] = topf;
    if (!wson[u]) // 沒兒子時直接結束
    {
        return ;
    }
    dfs2(wson[u], topf); // 先處理重兒子
    for (auto x : son[u])
    {
        if (x.fi == wson[u] || x.fi == fa[u]) //只處理輕兒子
        {
            continue;
        }
        dfs2(x.fi, x.fi); // 每個輕兒子以自己為top
    }
}

struct node
{
    int l, r;
    int sum;
    int laze;
} segment_tree[maxn << 2];

void build(int rt, int l, int r)
{
    segment_tree[rt].l = l;
    segment_tree[rt].r = r;
    segment_tree[rt].laze = inf;
    segment_tree[rt].sum = inf;
    if (l == r)
    {
        return;
    }
    int mid = (l + r) >> 1;
    build(rt << 1, l, mid);
    build(rt << 1 | 1, mid + 1, r);
}
void push_down(int rt)
{
    if (segment_tree[rt].laze != inf)
    {
        int num = segment_tree[rt].laze;
        segment_tree[rt << 1].sum = min(num, segment_tree[rt << 1].sum);
        segment_tree[rt << 1].laze = min(num, segment_tree[rt << 1].laze);
        segment_tree[rt << 1 | 1].sum = min(num, segment_tree[rt << 1 | 1].sum);
        segment_tree[rt << 1 | 1].laze = min(num, segment_tree[rt << 1 | 1].laze);
        segment_tree[rt].laze = inf;
    }
}
void update(int rt, int l, int r, int w)
{
    if (segment_tree[rt].r < l || segment_tree[rt].l > r)
        return ;
    if (l <= segment_tree[rt].l && segment_tree[rt].r <= r)
    {
        segment_tree[rt].sum = min(segment_tree[rt].sum, w);
        segment_tree[rt].laze = min(segment_tree[rt].laze, w);
        return ;
    }
    push_down(rt);
    int mid = (segment_tree[rt].l + segment_tree[rt].r) >> 1;
    if (r <= mid)
    {
        return update(rt << 1, l, r, w);
    } else
    {
        if (l > mid)
        {
            update(rt << 1 | 1, l, r, w);
        }
        else
        {
            update(rt << 1, l, r, w);
            update(rt << 1 | 1, l, r, w);
        }
    }
}

int query(int rt, int l, int r)
{
    if (segment_tree[rt].r < l || segment_tree[rt].l > r)
        return inf;
    if (segment_tree[rt].l >= l && r >= segment_tree[rt].r)
    {
        return segment_tree[rt].sum;
    }
    push_down(rt);
    int mid = (segment_tree[rt].l + segment_tree[rt].r) >> 1;
    if (r <= mid)
    {
        return query(rt << 1, l, r);
    } else
    {
        if (l > mid)
            return query(rt << 1 | 1, l, r);
        else
            return min(query(rt << 1, l, r), query(rt << 1 | 1, l, r));
    }
}


void uprange(int x, int y, int num)
{
    while (top[x] != top[y])
    {
        if (dep[top[x]] < dep[top[y]])
        {
            swap(x, y);
        }
        update(1, id[top[x]], id[x], num);
        x = fa[top[x]];
    }
    if (dep[x] > dep[y])
        swap(x, y);
    update(1, id[x] + 1, id[y], num);
}

int qrange(int x, int y)
{
    int ans = inf;
    if (top[x] != top[y])
    {
        if (dep[top[x]] < dep[top[y]])
        {
            swap(x, y);
        }
        ans = min(ans, query(1, id[top[x]], id[x]));
        x = fa[top[x]];
    } else
    {
        if (dep[x] > dep[y])
            swap(x, y);
        ans = min(ans, query(1, id[x] + 1, id[y]));
    }
    return ans;
}
struct edge
{
    int f, t, val, id;
    bool operator < (const edge& bb) const
    {
        return val < bb.val;
    }
} info[maxn];
int far[maxn];
int dsu_sz[maxn];
void dsu_init(int n)
{
    repd(i, 0, n)
    {
        far[i] = i;
        dsu_sz[i] = 1;
    }
}
int findpar(int x)
{
    if (x == far[x])
    {
        return x;
    } else
    {
        return far[x] = findpar(far[x]);
    }
}
void mg(int x, int y)
{
    x = findpar(x);
    y = findpar(y);
    if (x == y)
        return;
    if (dsu_sz[x] > dsu_sz[y])
    {
        dsu_sz[x] += dsu_sz[y];
        far[y] = x;
    } else
    {
        dsu_sz[y] += dsu_sz[x];
        far[x] = y;
    }
}
bool isok = 0;
int ans = 0;
bool vis[maxn];
void solve()
{
    sort(info + 1, info + 1 + m);
    dsu_init(n);
    int cnt = 0;
    repd(i, 1, m)
    {
        int x = findpar(info[i].f);
        int y = findpar(info[i].t);
        int w = info[i].val;
        if (x != y)
        {
            ans += w;
            cnt++;
            son[info[i].f].push_back(mp(info[i].t, w));
            son[info[i].t].push_back(mp(info[i].f, w));
            mg(x, y);
        } else
        {
            vis[info[i].id] = 1;
        }
    }
    isok = cnt == n - 1;
}
edge ed[maxn];
int main()
{
    cin >> n >> m;
    root = 1;
    int u, v, val;
    repd(i, 1, m)
    {
        cin >> u >> v >> val;
        info[i] = (edge) {u, v, val, i};
        ed[i] = info[i];
    }
    solve();
    if (isok == 0)
    {
        int q;
        cin >> q;
        while (q--)
        {
            int i;
            cin >> i;
            cout << "Not connected" << endl;
        }
        return 0;
    }
    dfs1(root, 0, 1);
    dfs2(root, root);
    build(1, 1, n);
    repd(i, 1, m)
    {
        if (!vis[i])
            continue;
        auto e = ed[i];
        uprange(e.t, e.f, e.val);
    }
    int q, op, x, y;
    cin >> q;
    while (q--)
    {
        cin >> op;
        x = ed[op].f;
        y = ed[op].t;
        if (vis[op])
        {
            cout << ans << endl;
            continue;
        }
        int res = qrange(x, y);
        if (res == inf)
        {
            cout << "Not connected" << endl;
        } else
        {
            cout << ans - ed[op].val + res << endl;
        }
    }

    return 0;
}