题目:求一个无向图的严格次小生成树(即次小生成树的边权和严格小于最小生成树的边权和)

首先求出图中的最小生成树。任意加一条树外边都会导致环的出现。我们现在目标是在树外边集合B中,找到边b∈B,a∈b所在环,b->weight - a->weight最小且不为0。

首先,依题意,a->weight应当是环内所有边中最大或第二大(最大可能a->weight==b->weight)的。如何找呢?我们采用树上倍增的方法。定义cur->Elder[k]为cur的第k辈祖先,MaxW[k]为cur与cur->Elder[k]路径中的最长边,MaxW2[k]为cur与cur->Elder[k]路径中的严格次长边(MaxW2[k]<MaxW[k])。枚举b时,求b->From和b->To的最近公共祖先。因为求LCA的过程基础便是cur=cur->MaxW[k],于是取在此过程中MaxW与MaxW2的最大值,便可求出答案。

如何求MaxW和MaxW2?有递归式:

cur->MaxW[i] = max(cur->MaxW[i - ], cur->Elder[i - ]->MaxW[i - ]);

        if (cur->MaxW[i - ] == cur->Elder[i - ]->MaxW[i - ])
cur->MaxW2[i] = max(cur->MaxW2[i - ], cur->Elder[i - ]->MaxW2[i - ]);
if (cur->MaxW[i - ] < cur->Elder[i - ]->MaxW[i - ])
cur->MaxW2[i] = max(cur->MaxW[i - ], cur->Elder[i - ]->MaxW2[i - ]);
if (cur->MaxW[i - ] > cur->Elder[i - ]->MaxW[i - ])
cur->MaxW2[i] = max(cur->MaxW2[i - ], cur->Elder[i - ]->MaxW[i - ]);

初值:

cur->MaxW[] = cur->ToFa ? cur->ToFa->Weight : ;
cur->MaxW2[] = -INF;

完整代码:

#include <cstdio>
#include <cstring>
#include <cassert>
#include <algorithm>
#include <cmath>
using namespace std; #define LOOP(i,n) for(int i=1; i<=n; i++)
const int MAX_NODE = , MAX_EDGE = * , MAX_FA = ,
INF = 0x3f3f3f3f; struct Node;
struct Edge; struct Node
{
int Id, Depth;
Edge *Head, *ToFa;
Node *Elder[MAX_FA], *Prev;
int MaxW[MAX_FA], MaxW2[MAX_FA];
}_nodes[MAX_NODE];
Node *GRoot;
int _vCount; struct Edge
{
Node *From, *To;
Edge *Next, *Rev;
int Weight;
bool InTree;
Edge(){}
Edge(Node *from, Node *to, Edge *next, int weight)
:From(from),To(to),Next(next),Weight(weight),InTree(false){}
}*_edges[MAX_EDGE];
int _eCount; int Log2(int x)
{
int cnt = ;
while (x /= )
cnt++;
return cnt;
} void Init(int vCount)
{
_eCount = ;
_vCount = vCount;
GRoot = + _nodes;
memset(_nodes, , sizeof(_nodes));
} Edge *AddEdge(Node *from, Node *to, int w)
{
Edge *e = _edges[++_eCount] = new Edge(from, to, from->Head, w);
from->Head = e;
return e;
} void Build(int uId, int vId, int w)
{
Node *u = uId + _nodes, *v = vId + _nodes;
u->Id = uId;
v->Id = vId;
Edge *e1 = AddEdge(u, v, w), *e2 = AddEdge(v, u, w);
e1->Rev = e2;
e2->Rev = e1;
} Node *GetRoot(Node *cur)
{
return cur->Prev ? cur->Prev = GetRoot(cur->Prev) : cur;
} void Join(Node *a, Node *b)
{
a->Prev = b;
} bool CmpEdge(Edge *a, Edge *b)
{
return a->Weight < b->Weight;
} long long MinW;
void Kruskal()
{
MinW = ;
sort(_edges + , _edges + _eCount + , CmpEdge);
int ans = , cnt = ;
LOOP(i, _eCount)
{
if (cnt == _vCount)
break;
Edge *e = _edges[i];
Node *root1 = GetRoot(e->From), *root2 = GetRoot(e->To);
if (root1 != root2)
{
cnt++;
e->InTree = true;
MinW += (long long)e->Weight;
Join(root1, root2);
}
}
} void Dfs(Node *cur)
{
cur->MaxW[] = cur->ToFa ? cur->ToFa->Weight : ;
cur->MaxW2[] = -INF;
int topFa = Log2(cur->Depth);
for (int i = ; i <= topFa && cur->Elder[i - ]; i++)
{
cur->Elder[i] = cur->Elder[i - ]->Elder[i - ];
cur->MaxW[i] = max(cur->MaxW[i - ], cur->Elder[i - ]->MaxW[i - ]); if (cur->MaxW[i - ] == cur->Elder[i - ]->MaxW[i - ])
cur->MaxW2[i] = max(cur->MaxW2[i - ], cur->Elder[i - ]->MaxW2[i - ]);
else if (cur->MaxW[i - ] < cur->Elder[i - ]->MaxW[i - ])
cur->MaxW2[i] = max(cur->MaxW[i - ], cur->Elder[i - ]->MaxW2[i - ]);
else if (cur->MaxW[i - ] > cur->Elder[i - ]->MaxW[i - ])
cur->MaxW2[i] = max(cur->MaxW2[i - ], cur->Elder[i - ]->MaxW[i - ]);
}
for (Edge *e = cur->Head; e; e = e->Next)
{
if (!e->To->Depth && (e->InTree || e->Rev->InTree))
{
e->To->ToFa = e;
e->To->Elder[] = cur;
e->To->Depth = cur->Depth + ;
Dfs(e->To);
}
}
} void GetReady()
{
GRoot->Depth = ;
Dfs(GRoot);
} void Update(int& ans, Node *a, int i, int w)
{
ans = max(ans, a->MaxW[i] < w ? a->MaxW[i] : a->MaxW2[i]);
} int GetAltEdgeW(Edge *e)
{
int ans = -INF, w = e->Weight;
Node *a = e->From, *b = e->To;
if (a->Depth < b->Depth)
swap(a, b);
for (int i = Log2(a->Depth-b->Depth); i >= ; i--)
{
if (a->Elder[i] && a->Elder[i]->Depth >= b->Depth)
{
Update(ans, a, i, w);
a = a->Elder[i];
}
}
assert(a->Depth == b->Depth);
if (a == b)
return ans;
for (int i = Log2(a->Depth); i >= ; i--)
{
if (a->Elder[i] && a->Elder[i] != b->Elder[i])
{
Update(ans, a, i, w);
Update(ans, b, i, w);
a = a->Elder[i];
b = b->Elder[i];
}
}
Update(ans, a, , w);
Update(ans, b, , w);
return ans;
} long long Proceed()
{
int delta = INF;
for (int i = ; i <= _eCount; i++)
if (!_edges[i]->InTree && !_edges[i]->Rev->InTree)
delta = min(delta, _edges[i]->Weight - GetAltEdgeW(_edges[i]));
return (long long)delta + MinW;
} int main()
{
#ifdef _DEBUG
freopen("c:\\noi\\source\\input.txt", "r", stdin);
#endif
int totNode, totEdge, uId, vId, w;
scanf("%d%d", &totNode, &totEdge);
Init(totNode);
for (int i = ; i <= totEdge; i++)
{
scanf("%d%d%d", &uId, &vId, &w);
Build(uId, vId, w);
}
Kruskal();
GetReady();
printf("%lld\n", Proceed());
return ;
}

luogu4180 次小生成树Tree 树上倍增的更多相关文章

  1. [bzoj1977][BeiJing2010组队]次小生成树 Tree——树上倍增+lca

    Brief Description 求一个无向图的严格次小生成树. Algorithm Design 考察最小生成树的生成过程.对于一个非树边而言,如果我们使用这一条非树边去替换原MST的路径上的最大 ...

  2. 【BZOJ1977】[BeiJing2010组队]次小生成树 Tree 最小生成树+倍增

    [BZOJ1977][BeiJing2010组队]次小生成树 Tree Description 小 C 最近学了很多最小生成树的算法,Prim 算法.Kurskal 算法.消圈算法等等. 正当小 C ...

  3. [BeiJing2010组队]次小生成树 Tree

    1977: [BeiJing2010组队]次小生成树 Tree Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 5168  Solved: 1668[S ...

  4. BZOJ 1977: [BeiJing2010组队]次小生成树 Tree( MST + 树链剖分 + RMQ )

    做一次MST, 枚举不在最小生成树上的每一条边(u,v), 然后加上这条边, 删掉(u,v)上的最大边(或严格次大边), 更新答案. 树链剖分然后ST维护最大值和严格次大值..倍增也是可以的... - ...

  5. 1977: [BeiJing2010组队]次小生成树 Tree

    1977: [BeiJing2010组队]次小生成树 Tree https://lydsy.com/JudgeOnline/problem.php?id=1977 题意: 求严格次小生成树,即边权和不 ...

  6. 洛谷P4180 [Beijing2010组队]次小生成树Tree(最小生成树,LCT,主席树,倍增LCA,倍增,树链剖分)

    洛谷题目传送门 %%%TPLY巨佬和ysner巨佬%%% 他们的题解 思路分析 具体思路都在各位巨佬的题解中.这题做法挺多的,我就不对每个都详细讲了,泛泛而谈吧. 大多数算法都要用kruskal把最小 ...

  7. (luogu4180) [Beijing2010组队]次小生成树Tree

    严格次小生成树 首先看看如果不严格我们怎么办. 非严格次小生成树怎么做 由此,我们发现一个结论,求非严格次小生成树,只需要先用kruskal算法求得最小生成树,然后暴力枚举非树边,替换路径最大边即可. ...

  8. BZOJ 1977: [BeiJing2010组队]次小生成树 Tree 倍增 最小生成树

    好吧我太菜了又调了一晚上...QAQ 先跑出最小生成树,标记树边,再用树上倍增的思路,预处理出: f[u][i] :距离u为2^i的祖先 h[u][i][0/1] :距u点在2^i范围内的最长边和次长 ...

  9. BZOJ 1977[BeiJing2010组队]次小生成树 Tree - 生成树

    描述: 就是求一个次小生成树的边权和 传送门 题解 我们先构造一个最小生成树, 把树上的边记录下来. 然后再枚举每条非树边(u, v, val),在树上找出u 到v 路径上的最小边$g_0$ 和 严格 ...

随机推荐

  1. C#url相关知识

    C#中Url地址重定向的方法: 1:Response.Redirect(url); 这个跳转页面的方法跳转的速度不快,因为它要走2个来回(2次postback),但他可以跳转到任何页面,没有站点页面限 ...

  2. Objective-C—— Block

    OC Block 其实功能就类似C语言的函数指针,js中的闭包之类的.把代码块当做一个变量就行操作,有自己的变量和作用域. 简单看一下Block的语法和可能出现的问题: Block语法: block语 ...

  3. 运用<body>属性,渲染页面效果

    新建一个HTML5文件,为<body>标签添加样式,代码如下: 01 <!doctype html> 02 <html> 03 <head> 04 &l ...

  4. HTML 5的基本标签

    1.  文件开始标签<html> 在任何的一个HTML文件里,最先出现的HTML标签就是<html>,它用于表示该文件是以超文本标识语言(HTML)编写的.<html&g ...

  5. Python2X和Python3X 除法运算符的使用:

    首先注明:如果没有特别说明,以下内容都是基于python 3.4的. 1. /是精确除法,//是向下取整除法,%是求模 2. %求模是基于向下取整除法规则的 3. 四舍五入取整round, 向零取整i ...

  6. javaee utf-8文件的转换

    package Zy; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.Fil ...

  7. 【转载】intellij idea如何将web项目打成war包

    1.点击[File]->[Project Structure]菜单(或使用Shift+Ctrl+Alt+S快捷键),打开[Project Structure]窗口.如下图: 2.在[Projec ...

  8. Day 20 re模块(正则表达式)

    re模块 作用:取文本或者字符串内找你所需要的东西 import re re.findall(参数一,参数二,参数三) #暂时用到前两个,第一个为正则表达式,第二个为字符串,也就是被搜索的文本 ^元字 ...

  9. 【剑指Offer】6、旋转数组的最小数字

      题目描述:   把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转. 输入一个非减排序的数组的一个旋转,输出旋转数组的最小元素. 例如数组{3,4,5,1,2}为{1,2,3,4,5 ...

  10. 国庆day2

    a[问题描述]你是能看到第一题的 friends呢.—— hja世界上没有什么比卖的这 贵弹丸三还令人绝望事了,所以便么一道题.定义