[BJWC2010] 严格次小生成树
传送门:>Here<
给出一张带权无向图,求其严格次小生成树。($n \leq 10^5$)
解题思路
图的生成树有一个性质:将一条非树边加入后必定形成环。Kruscal求最小生成树其实就是一个贪心地过程:剔除每个环上最大的那一条边。
那么反过来,求次小生成树就是在某个环上换回最大边,删去原来最大边以保证增量最小的过程。由于题目要求严格,会碰到最大边等于原先最大边的情况,此时就删去原先严格次大边。
于是现在就是一个数据结构维护的问题了。预处理出最小生成树,维护树链上最大值和次大值,可以树剖或者在倍增LCA的过程中维护。
如果够无聊,LCT也可以。LCT求最小生成树不用预先对边排序,由于可以动态连边断边,是完全可以根据最小生成树的性质直接维护的——每发现一条边形成了环,如果更优就直接LinkCut更新,保证了全局的最优性。再在Splay上维护边权的最小值就解决了。
$Code$
/*By QiXingzhi*/
#include <cstdio>
#include <vector>
#include <algorithm>
#define N (100010)
#define M (300010)
#define INF (21474836470000)
#define Max(a,b) (((a)>(b)) ? (a) : (b))
#define Min(a,b) (((a)<(b)) ? (a) : (b))
typedef long long ll;
using namespace std;
#define int ll
inline void read(int &x){
x = ; int w = ; register int c = getchar();
while(c ^ '-' && (c < '' || c > '')) c = getchar();
if(c == '-') w = -, c = getchar();
while(c >= '' && c <= '') x = (x << ) +(x << ) + c - '', c = getchar(); x *= w;
}
struct Edge{
int x,y,z;
};
struct Graph{
int to,cost;
};
Edge a[M];
bool t_edge[M];
vector <Graph> G[N];
vector <Graph> Gt[N];
int n,m,krus,krus_num,x,y,ans;
int par[N],f[N][],gmax[N][],gsec[N][],fa[N],wei[N],depth[N];
inline void AddEdge(int u, int v, int w){
Graph e;
e.to = v;
e.cost = w;
G[u].push_back(e);
}
inline void AddEdge_2(int u, int v, int w){
Graph e;
e.to = v;
e.cost = w;
Gt[u].push_back(e);
}
inline void Swap(int& a, int &b){
int t = a;
a = b;
b = t;
}
inline bool kruscal_sort_comp(const Edge& a, const Edge& b){
return a.z < b.z;
}
int krus_find(int x){
if(par[x] == x) return x;
par[x] = krus_find(par[x]);
return par[x];
}
void build_tree(int x, int ff){
int sz = Gt[x].size();
int to;
for(int i = ; i < sz; ++i){
to = Gt[x][i].to;
if(to != ff){
fa[to] = x;
wei[to] = Gt[x][i].cost;
build_tree(to, x);
}
}
}
void lca_dfs(int x, int d){
depth[x] = d;
for(int k = ; (<<k) < d; ++k){
f[x][k] = f[f[x][k-]][k-];
gmax[x][k] = Max(gmax[x][k-], gmax[f[x][k-]][k-]);
if(gmax[x][k-] == gmax[f[x][k-]][k-]){
gsec[x][k] = Max(gsec[x][k-], gsec[f[x][k-]][k-]);
}
else if(gmax[x][k-] > gmax[f[x][k-]][k-]){
gsec[x][k] = Max(gsec[x][k-], gmax[f[x][k-]][k-]);
}
else if(gmax[x][k-] < gmax[f[x][k-]][k-]){
gsec[x][k] = Max(gmax[x][k-], gsec[f[x][k-]][k-]);
}
}
int sz = Gt[x].size();
int to;
for(int i = ; i < sz; ++i){
to = Gt[x][i].to;
if(to != f[x][]){
lca_dfs(to, d+);
}
}
}
inline void Update(int i){
int u = a[i].x, v = a[i].y, w = a[i].z, res;
int val1 = -INF, val2 = -INF;
if(depth[u] > depth[v]) Swap(u,v);
for(int k = ; k >= ; --k){
if(depth[u] <= depth[v] - (<<k)){
if(gmax[v][k] < val1){
val2 = Max(gmax[v][k], val2);
}
else if(gmax[v][k] == val1){
val2 = Max(val2, gsec[v][k]);
}
else if(gmax[v][k] > val1){
val2 = Max(gsec[v][k], val1);
}
val1 = Max(val1, gmax[v][k]);
v = f[v][k];
}
}
if(u != v){
for(int k = ; k >= ; --k){
if(f[u][k] != f[v][k]){
if(gmax[u][k] < val1){
val2 = Max(val2, gmax[u][k]);
}
else if(gmax[u][k] == val1){
val2 = Max(val2, gsec[u][k]);
}
else{
val2 = Max(val1, gsec[u][k]);
}
if(gmax[v][k] < val1){
val2 = Max(val2, gmax[v][k]);
}
else if(gmax[v][k] == val1){
val2 = Max(val2, gsec[v][k]);
}
else{
val2 = Max(val1, gsec[v][k]);
}
val1 = Max(val1, gmax[u][k]);
val1 = Max(val1, gmax[v][k]);
u = f[u][k];
v = f[v][k];
}
}
val1 = Max(val1, Max(gmax[u][], gmax[v][]));
if(val2 < gmax[u][] && gmax[u][] < val1) val2 = gmax[u][];
if(val2 < gmax[v][] && gmax[v][] < val1) val2 = gmax[v][];
}
if(val1 < w){
res = krus - val1 + w;
}
else if(val1 == w){
res = krus - val2 + w;
}
ans = Min(ans, res);
}
#undef int
int main(){
#define int ll
// freopen(".in","r",stdin);
read(n), read(m);
for(int i = ; i <= m; ++i){
read(a[i].x), read(a[i].y), read(a[i].z);
AddEdge(a[i].x,a[i].y,a[i].z);
AddEdge(a[i].y,a[i].x,a[i].z);
}
sort(a+, a+m+, kruscal_sort_comp);
for(int i = ; i <= n; ++i) par[i] = i;
for(int i = ; i <= m; ++i){
x = krus_find(a[i].x);
y = krus_find(a[i].y);
if(x != y){
par[x] = y;
++krus_num;
krus += a[i].z;
t_edge[i] = ;
AddEdge_2(a[i].x,a[i].y,a[i].z);
AddEdge_2(a[i].y,a[i].x,a[i].z);
}
if(krus_num >= n-) break;
}
build_tree(, );
for(int i = ; i <= n; ++i){
f[i][] = fa[i];
gmax[i][] = wei[i];
gsec[i][] = -INF;
}
lca_dfs(,);
ans = INF;
for(int i = ; i <= m; ++i){
if(!t_edge[i]){
Update(i);
}
}
printf("%lld",ans);
return ;
}
[BJWC2010] 严格次小生成树的更多相关文章
- [BJWC2010]严格次小生成树(LCA,最小生成树)
[BJWC2010]严格次小生成树 题目描述 小C最近学了很多最小生成树的算法,Prim算法.Kurskal算法.消圈算法等等.正当小C洋洋得意之时,小P又来泼小C冷水了.小P说,让小C求出一个无向图 ...
- P4180 [BJWC2010]严格次小生成树
P4180 [BJWC2010]严格次小生成树 P4180 题意 求出一个无向联通图的严格次小生成树.严格次小生成树的定义为边权和大于最小生成树的边权和但不存在另一棵生成树的边权和在最小生成树和严格次 ...
- 【题解】洛谷P4180 [BJWC2010] 严格次小生成树(最小生成树+倍增求LCA)
洛谷P4180:https://www.luogu.org/problemnew/show/P4180 前言 这可以说是本蒟蒻打过最长的代码了 思路 先求出此图中的最小生成树 权值为tot 我们称这棵 ...
- 洛谷 P4180 【模板】严格次小生成树[BJWC2010]【次小生成树】
严格次小生成树模板 算法流程: 先用克鲁斯卡尔求最小生成树,然后给这个最小生成树树剖一下,维护边权转点权,维护最大值和严格次大值. 然后枚举没有被选入最小生成树的边,在最小生成树上查一下这条边的两端点 ...
- BZOJ1977/LuoguP4180【模板】严格次小生成树[BJWC2010] (次小生成树)
这道题本身思维难度不大,但综合性强,细节多 在其上浪一个早上,你的 最小生成树 树链剖分 线段树 DEBUG能力... 都大幅提升 细节与思路都在代码里面了. 欢迎hack. #include< ...
- P4180 【模板】严格次小生成树[BJWC2010]
P4180 [模板]严格次小生成树[BJWC2010] 倍增(LCA)+最小生成树 施工队挖断学校光缆导致断网1天(大雾) 考虑直接枚举不在最小生成树上的边.但是边权可能与最小生成树上的边相等,这样删 ...
- 【洛谷】4180:【模板】严格次小生成树[BJWC2010]【链剖】【线段树维护最大、严格次大值】
P4180 [模板]严格次小生成树[BJWC2010] 题目描述 小C最近学了很多最小生成树的算法,Prim算法.Kurskal算法.消圈算法等等.正当小C洋洋得意之时,小P又来泼小C冷水了.小P说, ...
- 「LuoguP4180」 【模板】严格次小生成树[BJWC2010](倍增 LCA Kruscal
题目描述 小C最近学了很多最小生成树的算法,Prim算法.Kurskal算法.消圈算法等等.正当小C洋洋得意之时,小P又来泼小C冷水了.小P说,让小C求出一个无向图的次小生成树,而且这个次小生成树还得 ...
- Luogu P4180 【模板】严格次小生成树[BJWC2010]
P4180 [模板]严格次小生成树[BJWC2010] 题意 题目描述 小\(C\)最近学了很多最小生成树的算法,\(Prim\)算法.\(Kurskal\)算法.消圈算法等等.正当小\(C\)洋洋得 ...
随机推荐
- 如何用CSS3画出一个立体魔方?
前言 最近在写<动画点点系列>文章,上一期分享了< 手把手教你如何绘制一辆会跑车 >,本期给大家带来是结合CSS3画出来的一个立体3d魔方,结合了js让你随心所欲想怎么转,就怎 ...
- ubuntu 添加开机启动服务
新建umpserver.service [Unit] Description=UMPServer After=syslog.target network.target remote-fs.target ...
- python实现一个二分查找
二分查找: 二分查找也称折半查找(Binary Search),它是一种效率较高的查找方法.但是,折半查找要求线性表必须采用顺序存储结构,而且表中元素按关键字有序排列 查找过程: 首先,假设表中元素是 ...
- PT与PX区别
字体大小的设置单位,常用的有2种:px.pt.这两个有什么区别呢? 先搞清基本概念:px就是表示pixel,像素,是屏幕上显示数据的最基本的点: pt就是point,是印刷行业常用单位,等于1/72英 ...
- PAT L3-016 二叉搜索树的结构
https://pintia.cn/problem-sets/994805046380707840/problems/994805047903240192 二叉搜索树或者是一棵空树,或者是具有下列性质 ...
- Jmeter之Constant Timer与constant throughput timer的区别(转)
当放置Constant Timer于两个http请求之间,那么它代表的含义是:在上一个请求发出至完成后, 开始Contant Timer指定的时间,最后再发出第二个请求.它并不是代表两个请求之间的发送 ...
- 通用模块设计UMD
https://leohxj.gitbooks.io/front-end-database/content/javascript-modules/about-umd.html UMD(universa ...
- B站弹幕姬(🐔)分析与开发(上篇)
辞职之后 休息了一段时间,最近准备开始恢复去工作的状态了,所以搞点事情来练练手.由于沉迷b站女妆大佬想做个收集弹幕的然后根据弹幕自动回复一些弹幕的东西.网上搜了一下有个c#的版本,感觉还做得不错,于是 ...
- HTML5 History API & URL 重定向
HTML5 History API & URL 重定向 disabled server url redirect https://developer.mozilla.org/en-US/doc ...
- DBX error:Driver could not be properly initialized .... 解决办法
系统: win7 64位+ MySql 将libmysql.dll和Dbxmys.dll 拷到 C:\Windows\SysWOW64 目录. ( 64位系统) 32位则拷到 c:\wind ...