zoukankan      html  css  js  c++  java
  • [BJWC2010]严格次小生成树

    传送门


    这道题以前我没做明白,今天再看看发现竟然挺好理解的。


    首先我们求出最小生成树,这样我们只需要在原树上改动一条边就可以得到次小生成树了。
    具体怎么改呢?已经有(n - 1)条边参与构成最小生成树了,我们枚举剩下的(m - n + 1)条边。记这条边的两个端点为(x)(y),权值为(w),那么我们只要尝试用这条边替换(x)(y)的树上路径的其中一条边就行了。值得注意的是,(w)一定大于等于路径上的任意一条边,因为如果小于的话,就可以把那条边删了,把(w)加进来构成最小生成树。


    接下来只要做两件事:记路径上最大的边权为(w_{max}),如果(w > w_{max}),那么直接替换并尝试更新答案;否则只可能(w = w_{max}),那么就要用(w)替换路径上的次大值。
    这样枚举(m - n + 1)条边后,选择替换后的最小值就是答案。


    所以我们要做的就是怎么求出路径上的最小值和次小值。
    树上的路径,自然会想到拆成(x)到lca,和lca到(y)的两条路径。那么可以在求lca的倍增数组的时候顺便把最大值和次大值维护出来。
    (Max[0][i][x])表示(x)往上(2 ^ i)长度的路径中的最大值,(Max[1][i][x])为对应的次大值,现在考虑用(i - 1)来更新(i)
    对于最大值来说,自然是

    [Max[0][i][x] = max {Max[0][i - 1][x], Max[0][i - 1][fa[i - 1][x]] } ]

    对于次大值的维护,要比较两个(i-1)段的最大值哪一个大:

    [egin{align*} Max[1][i][x] = max {Max[1][i - 1][x], Max[0][i - 1][fa[i - 1][x]] }(Max[0][i-1][x] > Max[0][i - 1][fa[i - 1][x]]) \ = max {Max[0][i - 1][x], Max[1][i - 1][fa[i - 1][x]] }(Max[0][i-1][x] > Max[0][i - 1][fa[i - 1][x]]) \ = max {Max[1][i - 1][x], Max[1][i - 1][fa[i - 1][x]] }(Max[0][i-1][x] = Max[0][i - 1][fa[i - 1][x]]) end{align*} ]

    处理完这些后,我们在求lca的时候就可以仿照倍增的思路求出路径上的最大值和次大值了。


    代码实现推荐用一个pair存最大值和次大值。因为无论是预处理还是倍增的时候,求最大值和次大值的逻辑是一样的,所以可以用一个函数封装起来,返回pair类型。

    #include<cstdio>
    #include<iostream>
    #include<cmath>
    #include<algorithm>
    #include<cstring>
    #include<cstdlib>
    #include<cctype>
    #include<vector>
    #include<queue>
    #include<assert.h>
    #include<ctime>
    using namespace std;
    #define enter puts("") 
    #define space putchar(' ')
    #define Mem(a, x) memset(a, x, sizeof(a))
    #define In inline
    #define forE(i, x, y) for(int i = head[x], y; ~i && (y = e[i].to); i = e[i].nxt)
    typedef long long ll;
    typedef double db;
    const ll INF = 1e18;
    const db eps = 1e-8;
    const int maxn = 1e5 + 5;
    const int maxm = 3e5 + 5;
    const int N = 18;
    In ll read()
    {
    	ll ans = 0;
    	char ch = getchar(), las = ' ';
    	while(!isdigit(ch)) las = ch, ch = getchar();
    	while(isdigit(ch)) ans = (ans << 1) + (ans << 3) + ch - '0', ch = getchar();
    	if(las == '-') ans = -ans;
    	return ans;
    }
    In void write(ll x)
    {
    	if(x < 0) x = -x, putchar('-');
    	if(x >= 10) write(x / 10);
    	putchar(x % 10 + '0');
    }
    In void MYFILE()
    {
    #ifndef mrclr
    	freopen(".in", "r", stdin);
    	freopen(".out", "w", stdout);
    #endif
    }
    
    int n, m;
    struct Edge
    {
    	int nxt, to, w;
    }e[maxn << 1];
    int head[maxn], ecnt = -1;
    In void addEdge(int x, int y, int w)
    {
    	e[++ecnt] = (Edge){head[x], y, w};
    	head[x] = ecnt;
    }
    
    int p[maxn];
    In int Find(int x) {return x == p[x] ? x : p[x] = Find(p[x]);}
    In void merge(int x, int y)
    {
    	int px = Find(x), py = Find(y);
    	if(px == py) return;
    	p[px] = py;
    }
    
    bool vis[maxm];
    struct edges
    {
    	int x, y, w;
    	In bool operator < (const edges& oth)const
    	{
    		return w < oth.w;
    	}
    }ed[maxm];
    
    #define pr pair<int, int>
    #define mp make_pair
    #define F first
    #define S second
    In pr calc(pr a, pr b)
    {
    	if(a.F == b.F) return mp(a.F, max(a.S, b.S));
    	else if(a.F > b.F) return mp(a.F, max(a.S, b.F));
    	else return mp(b.F, max(a.F, b.S));
    }
    
    int dep[maxn], fa[N + 2][maxn];
    pr Max[N + 2][maxn];
    In void dfs(int now, int _f)
    {
    	for(int i = 1; (1 << i) <= dep[now]; ++i)
    	{
    		fa[i][now] = fa[i - 1][fa[i - 1][now]];
    		Max[i][now] = calc(Max[i - 1][now], Max[i - 1][fa[i - 1][now]]);
    	}
    	forE(i, now, v)
    	{ 
    		if(v == _f) continue;
    		dep[v] = dep[now] + 1, fa[0][v] = now;
    		Max[0][v].F = e[i].w;
    		dfs(v, now);
    	}
    }
    
    In pr solve(int x, int y)
    {
    	pr ret = mp(0, 0);
    	if(dep[x] < dep[y]) swap(x, y);
    	for(int i = N; i >= 0; --i)
    		if(dep[fa[i][x]] > dep[y])
    		{
    			ret = calc(ret, Max[i][x]);
    			x = fa[i][x];
    		}
    	if(fa[0][x] == y) return ret;
    	ret = calc(ret, Max[0][x]);
    	x = fa[0][x];
    	for(int i = N; i >= 0; --i)
    		if((fa[i][x] ^ fa[i][y]) || !i)
    		{
    			ret = calc(ret, calc(Max[i][x], Max[i][y]));
    			x = fa[i][x], y = fa[i][y];
    		}
    	return ret;
    }
    
    int main()
    {
    //	MYFILE();
    	n = read(), m = read();
    	Mem(head, -1), ecnt = -1;
    	for(int i = 1; i <= n; ++i) p[i] = i;
    	for(int i = 1; i <= m; ++i)
    		ed[i].x = read(), ed[i].y = read(), ed[i].w = read();
    	sort(ed + 1, ed + m + 1);
    	ll sum = 0, ans = INF;
    	for(int i = 1; i <= m; ++i)
    	{
    		int x = ed[i].x, y = ed[i].y, w = ed[i].w;
    		if(Find(x) ^ Find(y))
    		{
    			merge(x, y), sum += w;
    			addEdge(x, y, w), addEdge(y, x, w); 
    			vis[i] = 1;
    		}
    	}
    	dep[1] = 1, dfs(1, 0);
    	for(int i = 1; i <= m; ++i)
    		if(!vis[i])
    		{
    			pr tp = solve(ed[i].x, ed[i].y);
    			if(ed[i].w > tp.F) ans = min(ans, sum - tp.F + ed[i].w);
    			else if(ed[i].w == tp.F) ans = min(ans, sum - tp.S + ed[i].w); 
    		}
    	write(ans), enter; 
    	return 0;	
    }
    
  • 相关阅读:
    UVA 408 (13.07.28)
    linux概念之用户,组及权限
    Java实现 蓝桥杯 历届试题 网络寻路
    Java实现 蓝桥杯 历届试题 约数倍数选卡片
    Java实现 蓝桥杯 历届试题 约数倍数选卡片
    Java实现 蓝桥杯 历届试题 约数倍数选卡片
    Java实现 蓝桥杯 历届试题 约数倍数选卡片
    Java实现 蓝桥杯 历届试题 约数倍数选卡片
    Java实现 蓝桥杯 历届试题 九宫重排
    Java实现 蓝桥杯 历届试题 九宫重排
  • 原文地址:https://www.cnblogs.com/mrclr/p/13767000.html
Copyright © 2011-2022 走看看