Description

小 C 最近学了很多最小生成树的算法,Prim 算法、Kurskal 算法、消圈算法等等。 正当小 C 洋洋得意之时,小 P 又来泼小 C 冷水了。小 P 说,让小 C 求出一个无向图的次小生成树,而且这个次小生成树还得是严格次小的,也就是说: 如果最小生成树选择的边集是 EM,严格次小生成树选择的边集是 ES,那么需要满足:(value(e) 表示边 e的权值)  这下小 C 蒙了,他找到了你,希望你帮他解决这个问题。

Input

第一行包含两个整数N 和M,表示无向图的点数与边数。 接下来 M行,每行 3个数x y z 表示,点 x 和点y之间有一条边,边的权值为z。

Output

包含一行,仅一个数,表示严格次小生成树的边权和。(数据保证必定存在严格次小生成树)

Sample Input

5 6
1 2 1
1 3 2
2 4 3
3 5 4
3 4 3
4 5 6

Sample Output

11

HINT

数据中无向图无自环; 50% 的数据N≤2 000 M≤3 000; 80% 的数据N≤50 000 M≤100 000; 100% 的数据N≤100 000 M≤300 000 ,边权值非负且不超过 10^9 。

 
首先应该想到的暴力是:枚举每一条树边,然后再枚举非树边。但是这样的时间复杂度是m^2只能过50%
后来可以考虑,先进行一遍最小生成树,然后对于每一条非树边,找到这个非树边起点、终点在树上位置,然后求出这两个点最短路径上的最大边,再以这条非树边进行替换。
如图,红边是非树边,我们需要找到u,v两点的树上路径的最长边,然后替换为红边
 
但是!如果这条最长边是等于那条非树边的,就不能替换。故此我们需要记录次长边并且保证次长边严格小于最长边。
那么问题来了:怎么记录最长、次长边?怎么找到两个点之间的最短距离?
显而易见,我们需要用倍增LCA来解决.没有学过LCA的同学可以先去写一下模板再做这道题。
我们记录tree[i][j]代表i点向上2^j的点得编号
maxx[i][j]表示i向上2^j的最大值,maxn[i][j]表示次大值。
根据LCA基本算法,我们能得出:tree[i][j]=tree[tree[i][j-1][j-1],其中maxn、maxx的具体维护看代码,主要是不重不漏(我开始漏了一种情况,调了一下午)
预处理出tree等数组,跑LCA时,记录ma为这条最短路上的最长路径,边跑变更新,但一定记住要时刻保证ma不等于更新的那条非树边!
最后ans=sum(最小生成树和)-ma+val[i](非树边边权)
代码如下:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
#define REP(i,k,n) for(int i=k;i<=n;i++)
#define in(a) a=read()
#define MAXN 100010
using namespace std;
inline int read(){
int x=,f=;
char ch=getchar();
for(;!isdigit(ch);ch=getchar())
if(ch=='-')
f=-;
for(;isdigit(ch);ch=getchar())
x=x*+ch-'';
return x*f;
}
queue <int> Q;
long long sum=,ans=(1ll<<);
int n,m;
int cnt,book[MAXN<<],f[MAXN];
int vis[MAXN],depth[MAXN],tree[MAXN][],maxx[MAXN][],maxn[MAXN][];
int total=,head[MAXN],to[MAXN<<],nxt[MAXN<<],val[MAXN<<];
struct node{
int x,y,z;
}l[MAXN<<];
bool cmp(node a,node b){
return a.z<b.z;
}
inline int getf(int k){
if(f[k]==k) return k;
return f[k]=getf(f[k]);
}
inline void adl(int a,int b,int c){
total++;
to[total]=b;
val[total]=c;
nxt[total]=head[a];
head[a]=total;
return ;
}
inline void BFS(){//预处理
Q.push();
depth[]=;
vis[]=;
while(!Q.empty()){
int u=Q.front();
Q.pop();
REP(j,,){
tree[u][j]=tree[tree[u][j-]][j-];
if(maxx[u][j-]>maxx[tree[u][j-]][j-]){
maxx[u][j]=maxx[u][j-];
maxn[u][j]=max(maxx[tree[u][j-]][j-],maxn[u][j-]);
}
if(maxx[u][j-]<maxx[tree[u][j-]][j-]){
maxx[u][j]=maxx[tree[u][j-]][j-];
maxn[u][j]=max(maxx[u][j-],maxn[tree[u][j-]][j-]);
}
if(maxx[u][j-]==maxx[tree[u][j-]][j-]){
maxx[u][j]=maxx[u][j-];
maxn[u][j]=max(maxn[u][j-],maxn[tree[u][j-]][j-]);
}
}
for(int e=head[u];e;e=nxt[e])
if(!vis[to[e]]){
vis[to[e]]=;
depth[to[e]]=depth[u]+;
tree[to[e]][]=u;
maxx[to[e]][]=val[e];
Q.push(to[e]);
}
}
return ;
}
inline int lca(int u,int v,int c){//lca
if(depth[u]<depth[v]) swap(u,v);
int d=depth[u]-depth[v];
int ma=-;
for(int i=;(<<i)<=d;i++)//提到同一高度
if((<<i)&d){
if(maxx[u][i]==c) ma=max(ma,maxn[u][i]);
else ma=max(ma,maxx[u][i]);
u=tree[u][i];
// cout<<u<<" "<<ma<<endl;
}
if(u==v){
if(ma==-) return ;
return ma;
}
for(int i=;i>=;i--)//两点开跑
if(tree[u][i]!=tree[v][i]){
if(maxx[u][i]==c && maxx[v][i]==c)
ma=max(ma,max(maxn[u][i],maxn[v][i]));
if(maxx[u][i]==c && maxx[v][i]!=c)
ma=max(ma,max(maxn[u][i],maxx[v][i]));
if(maxx[v][i]==c && maxx[u][i]!=c)
ma=max(ma,max(maxn[v][i],maxx[u][i]));
if(maxx[u][i]!=c && maxx[v][i]!=c)
ma=max(ma,max(maxx[v][i],maxx[u][i]));
u=tree[u][i];
v=tree[v][i];
}//最后lca是他们的父亲,所以要再更新一次
if(maxx[u][]==c && maxx[v][]==c)
ma=max(ma,max(maxn[u][],maxn[v][]));
if(maxx[u][]==c && maxx[v][]!=c)
ma=max(ma,max(maxn[u][],maxx[v][]));
if(maxx[v][]==c && maxx[u][]!=c)
ma=max(ma,max(maxn[v][],maxx[u][]));
if(maxx[u][]!=c && maxx[v][]!=c)
ma=max(ma,max(maxx[v][],maxx[u][]));
return ma;
}
int main(){
in(n);in(m);
REP(i,,n) f[i]=i;
REP(i,,m){
in(l[i].x);
in(l[i].y);
in(l[i].z);
}
sort(l+,l+m+,cmp);
REP(i,,m){
int f1=getf(l[i].x),f2=getf(l[i].y);
if(f1!=f2){
cnt++;
book[i]=;
f[f2]=f1;
sum+=(long long)l[i].z;
adl(l[i].x,l[i].y,l[i].z);
adl(l[i].y,l[i].x,l[i].z);
}
if(cnt==n-) break;
}
BFS();
REP(i,,m)
if(!book[i]){//枚举所有的非树边
// cout<<l[i].x<<" "<<l[i].y<<" "<<l[i].z<<endl;
// cout<<lca(l[i].x,l[i].y,l[i].z)<<endl;
ans=min(ans,sum-(long long)lca(l[i].x,l[i].y,l[i].z)+(long long)l[i].z);
}
cout<<ans;
}
 

bzoj1977 次小生成树的更多相关文章

  1. bzoj1977次小生成树(重要)

    #include<cstdio> #include<iostream> #include<cstring> #include<queue> #inclu ...

  2. 2018.09.15 bzoj1977:次小生成树 Tree(次小生成树+树剖)

    传送门 一道比较综合的好题. 由于是求严格的次小生成树. 我们需要维护一条路径上的最小值和次小值. 其中最小值和次小值不能相同. 由于不喜欢倍增我选择了用树链剖分维护. 代码: #include< ...

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

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

  4. 【次小生成树】bzoj1977 [BeiJing2010组队]次小生成树 Tree

    Description 小 C 最近学了很多最小生成树的算法,Prim 算法.Kurskal 算法.消圈算法等等. 正当小 C 洋洋得意之时,小 P 又来泼小 C 冷水了.小 P 说,让小 C 求出一 ...

  5. 严格次小生成树(Bzoj1977:[Beijing2010组队]次小生成树)

    非严格次小生成树 很简单,先做最小生成树 然后枚举没加入的边加入,替换掉这个环内最大的边 最后取\(min\) 严格次小生成树 还是一样的 可以考虑维护一个严格次大值 最大值和枚举的边相同就替换次大值 ...

  6. [BZOJ1977]严格次小生成树

    [问题描述] 小C最近学了很多最小生成树的算法,Prim算法.Kurskal算法.消圈算法等等. 正当小C洋洋得意之时,小P又来泼小C冷水了.小P说,让小C求出一个无向图的次小生成树,而且这个次小生成 ...

  7. [BZOJ1977][BeiJing2010组队]次小生成树

    题解: 首先要证明一个东西 没有重边的图上 次小生成树由任何一颗最小生成树替换一条边 但是我不会证啊啊啊啊啊啊啊 然后就很简单了 枚举每一条边看看能不能变 但有一个特殊情况就是,他和环上的最大值相等, ...

  8. 【bzoj1977】[BeiJing2010组队]次小生成树 Tree 最小生成树+权值线段树合并

    题目描述 求一张图的严格次小生成树的边权和,保证存在. 输入 第一行包含两个整数N 和M,表示无向图的点数与边数. 接下来 M行,每行 3个数x y z 表示,点 x 和点y之间有一条边,边的权值为z ...

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

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

随机推荐

  1. 使用Burpsuite爆破弱口令教工号

    使用Burpsuite爆破弱口令教工号 发表于 2015-11-18   |   分类于 Burpsuite  |   1条评论  |   26次阅读 准备 所谓工欲善其事,必先利其器,首先当然是要下 ...

  2. WAMP允许外部访问的修改方法

    apache配置文件httpd.conf里的 "Require local"改" Require all granted"

  3. 使用 Xtrabackup 在线对MySQL做主从复制【转】

    1. 说明 1.1 xtrabackup mysqldump对于导出10G以下的数据库或几个表,还是适用的,而且更快捷.一旦数据量达到100-500G,无论是对原库的压力还是导出的性能,mysqldu ...

  4. js日期工具

    /** * 日期工具类 */ define(function(require, exports, module) { var constants = require("constants&q ...

  5. Linux 上配置 NTP SERVER

    在CENTOS 6.2上面安装配置NTP SERVER 安装NTP:yum install ntp 配置时间源vi /etc/ntp.confserver 210.72.145.44server nt ...

  6. Deep Learning基础--理解LSTM网络

    循环神经网络(RNN) 人们的每次思考并不都是从零开始的.比如说你在阅读这篇文章时,你基于对前面的文字的理解来理解你目前阅读到的文字,而不是每读到一个文字时,都抛弃掉前面的思考,从头开始.你的记忆是有 ...

  7. JSOI 2017 Round 1滚粗记

    day0 到常州一中报道,吃了午饭,好像这次有小火锅. 然后下午听JYY讲线性规划...好神啊. 晚上去试机,机子上没有npp,只有linux下的codeblocks,敲起来一顿一顿的...后来被迫使 ...

  8. jquery 通过 live() 方法附加的事件处理程序适用于匹配选择器的当前及未来的元素(比如由脚本创建的新元素)

    jquery 通过 live() 方法附加的事件处理程序适用于匹配选择器的当前及未来的元素(比如由脚本创建的新元素) $("ul").append("<li cla ...

  9. 《深入理解Java虚拟机》笔记--第四章、虚拟机性能监控与故障处理工具

    主要学习并记录在命令行中操作服务器时使用的六大命令工具,可视化工具JConsole和VisualVM在开发过程中熟悉. 一.jps:虚拟机进程状况工具(JVM Process Status Tool) ...

  10. day41 - 异步IO、协程

    目录 (见右侧目录栏导航) - 1. 前言- 2. IO的五种模型- 3. 协程    - 3.1 协程的概念- 4. Gevent 模块    - 4.1 gevent 基本使用    - 4.2 ...