zoukankan      html  css  js  c++  java
  • 【BJWC2010】次小生成树

    题目链接:https://www.luogu.com.cn/problem/P4180

    题目大意:给定一张含有 (m) 条边的无向带权图 , 求出这张图中边权之和严格大于最小生成树的次小生成树的边权之和

    solution

    笔者太鸽了 , 一连咕了三天 , 因此来补一下前两天的锅

    这道题的思路很显然 , 先求出这张图的最小生成树 , 记它的边权之和为(sum) , 再考虑剩下的 (m - n + 1) 条边 , 如果把第 (i) 条, 分别连接(a_i , b_i), 权值为(w_i)的边加入树中 , 显然组成的生成树的最小值为 (sum - maxleft{dist(u, v) ight} + w_i) (其中 (u) , (v)(path( a_i , b_i )) 上的点) , 但要注意 , 这道题要求的是严格次小生成树 , 如果 (maxleft{dist(u, v) ight} = w_i) 的话 , 求出的还是最小生成树 , 对于这种情况 , 还需要求出 (max_{second}left{dist(u, v) ight}) , 组成的生成树最小值为 (sum - max_{second}left{dist(u, v) ight} + w_i) , 最后比较每条边组成的生成树大小 , 取其中的最小值即可

    对于求 (maxleft{dist(u, v) ight} = w_i) 以及 (max_{second}left{dist(u, v) ight}) 的值 , 可以分别用倍增处理 , 十分套路 , 笔者就不加赘述了

    时间复杂度 : (O(mlogn))

    code

    #include<bits/stdc++.h>
    using namespace std;
    template <typename T> inline void read(T &FF) {
    	int RR = 1; FF = 0; char CH = getchar();
    	for(; !isdigit(CH); CH = getchar()) if(CH == '-') RR = -RR;
    	for(; isdigit(CH); CH = getchar()) FF = FF * 10 + CH - 48;
    	FF *= RR;
    }
    inline void file(string str) {
    	freopen((str + ".in").c_str(), "r", stdin);
    	freopen((str + ".out").c_str(), "w", stdout);
    }
    const int N = 1e5 + 10, Log = 21, M = 3e5 + 10;
    int n, m, mf[N], fa[N][Log + 1];
    long long g[N][Log + 1][2], wi[N << 1], ans = LONG_LONG_MAX, sum;
    int now, fst[N], nxt[N << 1], num[N << 1], dep[N], fl[M];
    struct edge{
    	int u, v, w;
    	friend bool operator < (edge ai, edge bi) {
    		return ai.w < bi.w;
    	}
    }path[M];
    int get_fa(int xi) {
    	return mf[xi] == xi ? xi : mf[xi] = get_fa(mf[xi]);
    }
    void add(int u, int v, int w) {
    	nxt[++now] = fst[u], fst[u] = now, num[now] = v, wi[now] = w;
    	nxt[++now] = fst[v], fst[v] = now, num[now] = u, wi[now] = w;
    }
    void pre_bz(int xi) {
    	dep[xi] = dep[fa[xi][0]] + 1;
    	for(int i = 1; i <= Log; i++) {
    		fa[xi][i] = fa[fa[xi][i - 1]][i - 1];
    		g[xi][i][0] = max(g[xi][i - 1][0], g[fa[xi][i - 1]][i - 1][0]);
    		if(g[xi][i - 1][0] == g[fa[xi][i - 1]][i - 1][0])
    			g[xi][i][1] = max(g[xi][i - 1][1], g[fa[xi][i - 1]][i - 1][1]);
    		else if(g[xi][i - 1][0] > g[fa[xi][i - 1]][i - 1][0])
    			g[xi][i][1] = max(g[xi][i - 1][1], g[fa[xi][i - 1]][i - 1][0]);
    		else g[xi][i][1] = max(g[xi][i - 1][0], g[fa[xi][i - 1]][i - 1][1]);
    	}
    	for(int i = fst[xi]; i; i = nxt[i])
    		if(num[i] != fa[xi][0]) {
    			fa[num[i]][0] = xi;
    			g[num[i]][0][0] = wi[i], g[num[i]][0][1] = LONG_LONG_MIN;
    			pre_bz(num[i]);
    		}
    }
    pair<long long, long long> fmax(int xi, int yi) {
    	pair<long long, long long> mans;
    	if(dep[xi] < dep[yi]) swap(xi, yi);
    	for(int i = Log; i >= 0; i--)
    		if(dep[fa[xi][i]] >= dep[yi]) {
    			if(mans.first < g[xi][i][0]) {
    				mans.second = max(mans.first, g[xi][i][1]);
    				mans.first = g[xi][i][0];
    			}
    			else if(mans.first > g[xi][i][0])
    				mans.second = max(mans.second, g[xi][i][0]);
    			else mans.second = max(mans.second, g[xi][i][1]);
    			xi = fa[xi][i]; //把往上跳这一步给漏了都有60pts 
    		}
    	if(xi == yi) return mans;
    	for(int i = Log; i >= 0; i--)
    		if(fa[xi][i] != fa[yi][i]) {
    			if(mans.first < g[xi][i][0]) {
    				mans.second = max(mans.first, g[xi][i][1]);
    				mans.first = g[xi][i][0];
    			}
    			else if(mans.first > g[xi][i][0])
    				mans.second = max(mans.second, g[xi][i][0]);
    			else mans.second = max(mans.second, g[xi][i][1]);
    			if(mans.first < g[yi][i][0]) {
    				mans.second = max(mans.first, g[yi][i][1]);
    				mans.first = g[yi][i][0];
    			}
    			else if(mans.first > g[yi][i][0])
    				mans.second = max(mans.second, g[yi][i][0]);
    			else mans.second = max(mans.second, g[yi][i][1]);
    			xi = fa[xi][i], yi = fa[yi][i]; //这一步也是
    		}
    	if(g[xi][0][0] > mans.first)
    		mans.second = mans.first, mans.first = g[xi][0][0];
    	else if(g[xi][0][0] < mans.first)
    		mans.second = max(mans.second, g[xi][0][0]);
    	if(g[yi][0][0] > mans.first)
    		mans.second = mans.first, mans.first = g[yi][0][0];
    	else if(g[yi][0][0] < mans.first)
    		mans.second = max(mans.second, g[yi][0][0]);
    	return mans;
    }
    int main() {
    	//file("");
    	read(n), read(m);
    	for(int i = 1; i <= m; i++)
    		read(path[i].u), read(path[i].v), read(path[i].w);
    	sort(path + 1, path + m + 1);
    	for(int i = 1; i <= n; i++) mf[i] = i;
    	for(int i = 1; i <= m; i++) {
    		if(get_fa(path[i].v) == get_fa(path[i].u)) {
    			fl[i] = true;
    			continue;
    		}
    		sum += path[i].w;
    		add(path[i].u, path[i].v, path[i].w);
    		mf[get_fa(path[i].v)] = get_fa(path[i].u);
    	}
    	g[1][0][1] = LONG_LONG_MIN;
    	pre_bz(1);
    	for(int i = 1; i <= m; i++)
    		if(fl[i]) {
    			pair<long long, long long> res = fmax(path[i].u, path[i].v);
    			if(path[i].w > res.first) ans = min(ans, sum - res.first + path[i].w);
    			else ans = min(ans, sum - res.second + path[i].w);
    		}
    	cout << ans << endl;
    	return 0;
    }
    
    
  • 相关阅读:
    二分图 洛谷P2055 [ZJOI2009]假期的宿舍
    并查集 洛谷P1640 [SCOI2010]连续攻击游戏
    贪心 洛谷P2870 Best Cow Line, Gold
    贪心 NOIP2013 积木大赛
    快速幂 NOIP2013 转圈游戏
    倍增LCA NOIP2013 货车运输
    树形DP 洛谷P2014 选课
    KMP UVA1328 Period
    动态规划入门 BZOJ 1270 雷涛的小猫
    KMP POJ 2752Seek the Name, Seek the Fame
  • 原文地址:https://www.cnblogs.com/magicduck/p/12253040.html
Copyright © 2011-2022 走看看