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

首先求出图中的最小生成树。任意加一条树外边都会导致环的出现。我们现在目标是在树外边集合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. B - Magnets

    Problem description Mad scientist Mike entertains himself by arranging rows of dominoes. He doesn't ...

  2. Could not create the view: An unexpected exception was thrown. Myeclipse空间报错

    我的路径D:\MyEclipse 10\.metadata\.plugins\org.eclipse.core.runtime\.settings 我也遇到过这个问题,就是工作空间的问题好像是删除你工 ...

  3. SQL Server中,with as使用介绍

    一.WITH AS的含义      WITH AS短语,也叫做子查询部分(subquery factoring),可以让你做很多事情,定义一个SQL片断,该SQL片断会被整个SQL语句所用到.有的时候 ...

  4. Dispatch Queues and Thread Safety

    Dispatch Queues and Thread Safety It might seem odd to talk about thread safety in the context of di ...

  5. SQLite 的使用

    private void button1_Click(object sender, EventArgs e) { //查询数据库内容并绑定 string sql= "select* from ...

  6. 去掉二级页面 tabs 菜单, 修改返回按钮

    imports: [ /*引入的模块 依赖的模块*/ BrowserModule, ComponentsModule, IonicModule.forRoot(MyApp,{ tabsHideOnSu ...

  7. Javaweb 使用Servlet技术改写用户登录 使用Filter技术解决中文乱码

    先把实验3的jsp页面复制过来: WebContent->WEB-INF->lib下面的jar包8.0版本也要记得复制: Java Resources->src下的 cn.edu.h ...

  8. eas左树右表基础资料界面引用为左树右表F7的简单方法

    age:   /** * 加载配件F7(左树右表) * @param F7Filed           要加载的F7控件 * @param ctx               界面上下文 * @单据 ...

  9. BZOJ 3744 Gty的妹子序列 (分块+树状数组+主席树)

    题面传送门 题目大意:给你一个序列,多次询问,每次取出一段连续的子序列$[l,r]$,询问这段子序列的逆序对个数,强制在线 很熟悉的分块套路啊,和很多可持久化01Trie的题目类似,用分块预处理出贡献 ...

  10. [pytorch学习]2. 官网60分钟教程摘要

    https://pytorch.org/tutorials/beginner/deep_learning_60min_blitz.html 1. Pytorch的基本单元,tensor,本质上和num ...