题面

传送门

题目大意:

给定一个无向连通带权图G,对于每条边(u,v,w)" role="presentation" style="position: relative;">(u,v,w)(u,v,w),求包含这条边的生成树大小的最小值

分析

包含这条边的生成树的大小如何表示呢?

先求出整张图的最小生成树大小tlen,对于每一条边(u,v,w)" role="presentation" style="position: relative;">(u,v,w)(u,v,w),我们最小生成树中去掉树上从u到v的路径上权值最大,最大值为mlen的一条边,再加上w,得到的一定是包含这条边的生成树大小的最小值tlen−mlen+w" role="presentation" style="position: relative;">tlen−mlen+wtlen−mlen+w

最小生成树大小tlen可用kruskal算法在O(mlog2m)" role="presentation" style="position: relative;">O(mlog2m)O(mlog2m)时间内求出

那么问题转化为求mlen,可用树上倍增法求解

树上倍增法的好处是在求LCA的同时可以维护更多的附加信息

在求LCA的过程中设fa[i][j]表示i的2j" role="presentation" style="position: relative;">2j2j辈祖先

可写出公式

fa[i][j]=fa[fa[i][j−1]][j−1]" role="presentation">fa[i][j]=fa[fa[i][j−1]][j−1]fa[i][j]=fa[fa[i][j−1]][j−1]

(即i的2j" role="presentation" style="position: relative;">2j2j辈祖先是i的2j−1" role="presentation" style="position: relative;">2j−12j−1辈祖先的2j−1" role="presentation" style="position: relative;">2j−12j−1辈祖先)

同理可写出最大长度

mlen[i][j]=max(mlen[i][j−1],mlen[mlen[i][j−1]][j−1])" role="presentation">mlen[i][j]=max(mlen[i][j−1],mlen[mlen[i][j−1]][j−1])mlen[i][j]=max(mlen[i][j−1],mlen[mlen[i][j−1]][j−1])

查询时类似LCA的查询即可,详情见代码

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cmath>
#define maxn 200005
#define maxm 200005
#define maxlog 32
using namespace std;
int n,m;
inline int qread(){
int x=0,sign=1;
char c=getchar();
while(c<'0'||c>'9'){
if(c=='-') sign=-1;
c=getchar();
}
while(c>='0'&&c<='9'){
x=x*10+c-'0';
c=getchar();
}
return x*sign;
}
struct edge{
int from;
int to;
int len;
int next;
int index;
edge(){ }
edge(int x,int y,int z,int i){
from=x;
to=y;
len=z;
index=i;
}
friend bool operator <(edge x,edge y){
return x.len<y.len;
}
};
edge G[maxm*2],MST[maxm*2];
int head[maxn];
int size=0;
void add_edge(int u,int v,int w){
size++;
MST[size].from=u;
MST[size].to=v;
MST[size].len=w;
MST[size].next=head[u];
head[u]=size;
}
int fset[maxn];
void set_init(){
for(int i=1;i<=n;i++) fset[i]=i;
}
int find(int x){
if(fset[x]==x) return x;
else{
fset[x]=find(fset[x]);
return fset[x];
}
}
long long kruskal(){
long long ans=0;
sort(G+1,G+1+m);
for(int i=1;i<=m;i++){
int fx=find(G[i].from);
int fy=find(G[i].to);
if(fx!=fy){
add_edge(G[i].from,G[i].to,G[i].len);
add_edge(G[i].to,G[i].from,G[i].len);
fset[fx]=fy;
ans+=G[i].len;
}
}
return ans;
} int deep[maxn],fa[maxn][maxlog];
long long mlen[maxn][maxlog];
int log2n;
void lca_init(){
queue<int>q;
q.push(1);
deep[1]=1; //初始化深度
while(!q.empty()){
int x=q.front();
q.pop();
for(int i=head[x];i;i=MST[i].next){//MST表示最小生成树的边
int y=MST[i].to;
if(deep[y]) continue;
deep[y]=deep[x]+1;
fa[y][0]=x;//fa和mlen的初始值
mlen[y][0]=MST[i].len;
for(int j=1;j<=log2n;j++){//倍增初始化
fa[y][j]=fa[fa[y][j-1]][j-1];
mlen[y][j]=max(mlen[y][j-1],mlen[fa[y][j-1]][j-1]);
}
q.push(y);
}
}
}
long long lca_query(int x,int y){
if(deep[x]>deep[y]) swap(x,y);
long long maxl=0;
for(int i=log2n;i>=0;i--){//先将x和y调整到同一深度
if(deep[fa[y][i]]>=deep[x]){
maxl=max(maxl,mlen[y][i]);//y上升同时更新maxl
y=fa[y][i];
}
}
if(x==y) return maxl;//如果LCA(x,y)=x,直接返回
for(int i=log2n;i>=0;i--){//x,y同时上升,直到差一条边相遇
if(fa[x][i]!=fa[y][i]){
maxl=max(maxl,max(mlen[x][i],mlen[y][i]));
x=fa[x][i];
y=fa[y][i];
}
}
maxl=max(maxl,max(mlen[x][0],mlen[y][0]));//最后再更新一次
return maxl;
}
long long ans[maxm];//便于按输入顺序输出
int main(){
int s,t,r;
n=qread();
m=qread();
for(int i=1;i<=m;i++){
s=qread();
t=qread();
r=qread();
G[i]=edge(s,t,r,i);
}
set_init();
long long tlen=kruskal();
log2n=log2(n)+1;
lca_init();
for(int i=1;i<=m;i++){
ans[G[i].index]=tlen+(long long)G[i].len-(long long)lca_query(G[i].from,G[i].to);//求生成树大小的最小值
}
for(int i=1;i<=m;i++){
printf("%I64d\n",ans[i]);
}
}

Codeforces 609E (Kruskal求最小生成树+树上倍增求LCA)的更多相关文章

  1. 【BZOJ 3551】[ONTAK2010] Peaks加强版 Kruskal重构树+树上倍增+主席树

    这题真刺激...... I.关于Kruskal重构树,我只能开门了,不过补充一下那玩意还是一棵满二叉树.(看一下内容之前请先进门坐一坐) II.原来只是用树上倍增求Lca,但其实树上倍增是一种方法,L ...

  2. 树上倍增求LCA及例题

    先瞎扯几句 树上倍增的经典应用是求两个节点的LCA 当然它的作用不仅限于求LCA,还可以维护节点的很多信息 求LCA的方法除了倍增之外,还有树链剖分.离线tarjan ,这两种日后再讲(众人:其实是你 ...

  3. [学习笔记] 树上倍增求LCA

    倍增这种东西,听起来挺高级,其实功能还没有线段树强大.线段树支持修改.查询,而倍增却不能支持修改,但是代码比线段树简单得多,而且当倍增这种思想被应用到树上时,它的价值就跟坐火箭一样,噌噌噌地往上涨. ...

  4. 树上倍增求LCA(最近公共祖先)

    前几天做faebdc学长出的模拟题,第三题最后要倍增来优化,在学长的讲解下,尝试的学习和编了一下倍增求LCA(我能说我其他方法也大会吗?..) 倍增求LCA: father[i][j]表示节点i往上跳 ...

  5. [算法]树上倍增求LCA

    LCA指的是最近公共祖先(Least Common Ancestors),如下图所示: 4和5的LCA就是2 那怎么求呢?最粗暴的方法就是先dfs一次,处理出每个点的深度 然后把深度更深的那一个点(4 ...

  6. 树上倍增求LCA详解

    LCA(least common ancestors)最近公共祖先 指的就是对于一棵有根树,若结点z既是x的祖先,也是y的祖先(不要告诉我你不知道什么是祖先),那么z就是结点x和y的最近公共祖先. 定 ...

  7. BZOJ 4242 水壶(BFS建图+最小生成树+树上倍增)

    题意 JOI君所居住的IOI市以一年四季都十分炎热著称. IOI市是一个被分成纵H*横W块区域的长方形,每个区域都是建筑物.原野.墙壁之一.建筑物的区域有P个,编号为1...P. JOI君只能进入建筑 ...

  8. [luogu3379]最近公共祖先(树上倍增求LCA)

    题意:求最近公共祖先. 解题关键:三种方法,1.st表 2.倍增法 3.tarjan 此次使用倍增模板(最好采用第一种,第二种纯粹是习惯) #include<cstdio> #includ ...

  9. CF 519E(树上倍增求lca)

    传送门:A and B and Lecture Rooms 题意:给定一棵树,每次询问到达点u,v距离相等的点有多少个. 分析:按情况考虑: 1.abs(deep[u]-deep[v])%2==1时, ...

随机推荐

  1. DevExpress v19.1新版亮点——WinForms篇(二)

    行业领先的.NET界面控件DevExpress v19.1终于正式发布,本站将以连载的形式介绍各版本新增内容.在本系列文章中将为大家介绍DevExpress WinForms v19.1中新增的一些控 ...

  2. html2canvas 把h5网页保存为图片 区域保存

    html2canvas 把h5网页保存为图片 想把一个网页得某些元素,绘制成图片保存,有些数据是接口动态加载的,所以不能UI给到图片,需要我们把api的数据也绘制到图片上 html2canvas这个插 ...

  3. Extjs中如何在一行textfield后面增加文字提示

    添加监听事件: listeners: {               render: function(obj) {                     var font=document.cre ...

  4. 理解Python中的__init__和__new__

    先来看一段代码: class A(object): # -> don't forget the object specified as base def __new__(cls): print ...

  5. node.js 的 中间件 初理解

    听说中间件还挺重要,下面梳理一下初认识: 中间件是什么?简单说说http请求服务的过滤,当交给函数处理之前先交给它处理.匹配后会终止,要想再匹配,得加: next. 中间件能解决什么问题?检测用户登录 ...

  6. Vue Vue项目目录结构梳理

    Vue项目目录结构梳理   by:授客 QQ:1033553122 1.   结构梳理   . ├── build/                      # webpack 配置文件: │   ...

  7. 小程序-登录-token

    1.前端调用wx.login()获取code值 2.前端通过调用wx.getUserInfo获取iv.rawData.signature.encryptedData等加密数据,传递给后端 3.服务器通 ...

  8. java构造方法和重写equals

    Cell的构造函数 package Test; import java.util.Objects; public class Cell { int a; int b; public int getA( ...

  9. 为什么阿里巴巴要禁用Executors创建线程池?

    作者:何甜甜在吗 juejin.im/post/5dc41c165188257bad4d9e69 看阿里巴巴开发手册并发编程这块有一条:线程池不允许使用Executors去创建,而是通过ThreadP ...

  10. BZOJ 1029 建筑抢修(贪心堆)

    原题代号:BZOJ 1029 原题链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1029 原题描述: 建筑抢修 小刚在玩JSOI提供的一个称之为 ...