1. 程式人生 > >BZOJ1977: [BeiJing2010組隊]次小生成樹 Tree

BZOJ1977: [BeiJing2010組隊]次小生成樹 Tree

++ spa ring mes code ref namespace urn sync

1977: [BeiJing2010組隊]次小生成樹 Tree

題意:求嚴格次小生成樹


我為什麽要單獨發這篇呢

因為愚蠢的我不停換寫法最後發現是因為沒開long long所以wa掉的

很簡單,次小生成樹是由mst換一條邊得到的

就是枚舉非樹邊,加入後會形成一個環,求環上的最大值和嚴格次大值與這條非樹邊換一換

用倍增可以做到O(mn)-->O(mlogn)

我寫了兩種求lca時同時求路徑上最大值/次大值的做法

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <set>
using namespace std;
typedef long long ll;
const int N = 1e5+5, M = 3e5+5;

int n, m;
struct edge {int v, ne, w;} e[M<<2];
int cnt, h[N];
inline void ins(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;
}

struct meow {
    int u, v, w;
    bool operator < (const meow &r) const {
        return w < r.w;
    }
} a[M];

bool mark[M];
namespace mst {
    int fa[N];
    int find(int x) {return x == fa[x] ? x : fa[x] = find(fa[x]);}
    ll kruskal() {
        for(int i=1; i<=n; i++) fa[i] = i;
        sort(a+1, a+1+m);
        int cnt = 0;
        ll ans = 0;
        for(int i=1; i<=m; i++) {
            int x = find(a[i].u), y = find(a[i].v);
            if(x != y) { //printf("use (%d, %d) %d\n", a[i].u, a[i].v, a[i].w);
                fa[y] = x, ans += a[i].w, mark[i] = 1;
                ins(a[i].u, a[i].v, a[i].w);
                if(++cnt == n-1) break;
            }
        }
        return ans;
    }
}

int deep[N], fa[N][20], vis[N];
#define fir first
#define sec second
pair<int, int> val[N][20];
pair<int, int> merge(pair<int, int> a, pair<int, int> b) {
    pair<int, int> ans;
    if(a.fir == b.fir) ans.fir = a.fir, ans.sec = max(a.sec, b.sec);
    else if(a.fir > b.fir) ans.fir = a.fir, ans.sec = max(a.sec, b.fir);
    else ans.fir = b.fir, ans.sec = max(a.fir, b.sec);
    /*
    ans.fir = max(a.fir, b.fir);
    if(a.fir == b.fir) ans.sec = max(a.sec, b.sec);
    else {
        ans.sec = min(a.fir, b.fir);
        ans.sec = max(ans.sec, max(a.sec, b.sec));
    }
    */
    return ans;
}
void dfs(int u) { 
    vis[u] = 1;
    for(int j=1; (1<<j)<=deep[u]; j++) {
        fa[u][j] = fa[fa[u][j-1]][j-1];
        val[u][j] = merge(val[u][j-1], val[fa[u][j-1]][j-1]);
    }
    for(int i=h[u]; i; i=e[i].ne) {
        int v = e[i].v;
        if(vis[v]) continue;
        deep[v] = deep[u]+1;
        fa[v][0] = u;
        val[v][0] = make_pair(e[i].w, -1);
        dfs(v);
    }
}

pair<int, int> lca(int x, int y) { 
    pair<int, int> ans(-1, -1);

    if(deep[x] < deep[y]) swap(x, y);
    int bin = deep[x] - deep[y];
    for(int j=0; j<=17; j++)
        if((1<<j) & bin) ans = merge(ans, val[x][j]), x = fa[x][j];
    
    if(x == y) return ans;
    for(int j=17; j>=0; j--)
        if(fa[x][j] != fa[y][j]) {
            ans = merge(ans, merge(val[x][j], val[y][j]));
            x = fa[x][j], y = fa[y][j];
        }
    ans = merge(ans, merge(val[x][0], val[y][0]));
    return ans;
}
int lca2(int x, int y) {
    if(deep[x] < deep[y]) swap(x, y);
    int bin = deep[x] - deep[y];
    for(int j=0; j<=17; j++) 
        if((1<<j) & bin) x = fa[x][j];
    if(x == y) return x;
    for(int j=17; j>=0; j--) 
        if(fa[x][j] != fa[y][j]) x = fa[x][j], y = fa[y][j];
    return fa[x][0];
}

pair<int, int> cal(int x, int r) {
    pair<int, int> ans(-1e9, -1e9);
    int bin = deep[x] - deep[r];
    for(int j=0; j<=17; j++)
        if((1<<j) & bin) ans = merge(ans, val[x][j]), x = fa[x][j];
    return ans;
}
int main() {
    freopen("in", "r", stdin);
    ios::sync_with_stdio(false); cin.tie(); cout.tie();

    cin >> n >> m;
    for(int i=1; i<=m; i++) {
        int u, v, w;
        cin >> u >> v >> w;
        a[i] = (meow) {u, v, w};
    }
    
    ll mn = mst::kruskal();
    dfs(1);
    int ans = 1e9+7;
    for(int i=1; i<=m; i++) if(!mark[i]) {
        int u = a[i].u, v = a[i].v, w = a[i].w;
        //pair<int, int> t = lca(u, v);
        //printf("hey (%d, %d) %d         %d %d\n", u, v, w, t.fir, t.sec);
        int r = lca2(u, v);
        pair<int, int> t = merge(cal(u, r), cal(v, r));
        if(w > t.fir) ans = min(ans, w - t.fir);
        if(w == t.fir) ans = min(ans, w - t.sec);
    }
    //printf("look %d %d\n", mn, ans);
    cout << mn + ans;
}

BZOJ1977: [BeiJing2010組隊]次小生成樹 Tree