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;
    }
    
    
  • 相关阅读:
    加载spring容器
    id生成工具类
    Spring基础之 反射(Reflection)
    XML解析之dom4j
    我的集合学习笔记--LinkedList
    验证身份证号的正确性
    String系列-----AbstractStringBuilder
    String系列-----String
    Java设计模式-迭代器模式
    Servlet的三个域对象
  • 原文地址:https://www.cnblogs.com/magicduck/p/12253040.html
Copyright © 2011-2022 走看看