Codeforces 609E (Kruskal求最小生成树+树上倍增求LCA)
题面
传送门
题目大意:
给定一个无向连通带权图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辈祖先
可写出公式
(即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辈祖先)
同理可写出最大长度
查询时类似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)的更多相关文章
- 【BZOJ 3551】[ONTAK2010] Peaks加强版 Kruskal重构树+树上倍增+主席树
这题真刺激...... I.关于Kruskal重构树,我只能开门了,不过补充一下那玩意还是一棵满二叉树.(看一下内容之前请先进门坐一坐) II.原来只是用树上倍增求Lca,但其实树上倍增是一种方法,L ...
- 树上倍增求LCA及例题
先瞎扯几句 树上倍增的经典应用是求两个节点的LCA 当然它的作用不仅限于求LCA,还可以维护节点的很多信息 求LCA的方法除了倍增之外,还有树链剖分.离线tarjan ,这两种日后再讲(众人:其实是你 ...
- [学习笔记] 树上倍增求LCA
倍增这种东西,听起来挺高级,其实功能还没有线段树强大.线段树支持修改.查询,而倍增却不能支持修改,但是代码比线段树简单得多,而且当倍增这种思想被应用到树上时,它的价值就跟坐火箭一样,噌噌噌地往上涨. ...
- 树上倍增求LCA(最近公共祖先)
前几天做faebdc学长出的模拟题,第三题最后要倍增来优化,在学长的讲解下,尝试的学习和编了一下倍增求LCA(我能说我其他方法也大会吗?..) 倍增求LCA: father[i][j]表示节点i往上跳 ...
- [算法]树上倍增求LCA
LCA指的是最近公共祖先(Least Common Ancestors),如下图所示: 4和5的LCA就是2 那怎么求呢?最粗暴的方法就是先dfs一次,处理出每个点的深度 然后把深度更深的那一个点(4 ...
- 树上倍增求LCA详解
LCA(least common ancestors)最近公共祖先 指的就是对于一棵有根树,若结点z既是x的祖先,也是y的祖先(不要告诉我你不知道什么是祖先),那么z就是结点x和y的最近公共祖先. 定 ...
- BZOJ 4242 水壶(BFS建图+最小生成树+树上倍增)
题意 JOI君所居住的IOI市以一年四季都十分炎热著称. IOI市是一个被分成纵H*横W块区域的长方形,每个区域都是建筑物.原野.墙壁之一.建筑物的区域有P个,编号为1...P. JOI君只能进入建筑 ...
- [luogu3379]最近公共祖先(树上倍增求LCA)
题意:求最近公共祖先. 解题关键:三种方法,1.st表 2.倍增法 3.tarjan 此次使用倍增模板(最好采用第一种,第二种纯粹是习惯) #include<cstdio> #includ ...
- CF 519E(树上倍增求lca)
传送门:A and B and Lecture Rooms 题意:给定一棵树,每次询问到达点u,v距离相等的点有多少个. 分析:按情况考虑: 1.abs(deep[u]-deep[v])%2==1时, ...
随机推荐
- @Component和@Bean以及@Autowired、@Resource
1. 有这么一个故事,从xml配置文件的bean说起 Spring用xml配置文件的时候(不知道阅读这篇文章的你用没用过,我用过一段时间,那是黑暗伤痛的回忆QQQ),一个xml配置文件里面有很多个 ...
- ES6数据结构Set、Map
一.Set数据结构 Set是无序的不可重复的多个value的集合体,Set结构是类似于数组结构,但是Set中的值都不能重复 常用的属性和方法 size:返回set实例的成员总数 add():添加某个值 ...
- Linux学习-NFS服务
一.NFS服务相关介绍 1.NFS简介 NFS (Network File System) 网络文件系统,基于内核的文件系统.Sun公司开发,通过使用NFS,用户和程序可以像访问本地文件一样访问远端系 ...
- css雪碧图-css精灵图
先将图片拼接在一张图上.类似实现的效果图 图片地址为合并后的图片地址,通过background-position调整背景图的位置.效果如: HTML: <div class="logo ...
- 解决json不能解析换行问题
今天遇到一个问题,当我读取数据库中某条带换行的数据时,解析错误. 解决方法是在存入数据库时对数据做处理,把换行换成其他字符.代码如下: remark = remark.replace(/\n/g,&q ...
- BZOJ 3357: [Usaco2004]等差数列 动态规划
Code: #include<bits/stdc++.h> #define setIO(s) freopen(s".in","r",stdin) # ...
- 【PowerOJ1736&网络流24题】飞行员配对方案问题(最小割)
题意: n<=100,要求输出方案 思路:准备把没刷的24题从头搞一遍 输出方案的话就在增广的时候记一下另一端的编号就好 #include<bits/stdc++.h> using ...
- 一本通&&洛谷 ——靶型数独——题解
题目传送 主要是搜索顺序不同导致效率千差万别. 联想人做数独的策略,总是先填可填数最少的那个空,再填选择第二少的...其实这种策略就造就了一个深度浅时分支也较少的搜索树.合适的搜索顺序再配合剪枝==A ...
- Node 资源
Node.js 首页 最新的 Node.js 核心文档 Node.js 博客 Node.js 职位公告板 Node.js 包管理器(npm)的首页
- #1126-JSP HTTP状态码
JSP HTTP状态码 HTTP请求与HTTP响应的格式相近,都有着如下结构: 以状态行+CRLF(回车换行)开始零行或多行头模块+CRLF一个空行,比如CRLF可选的消息体比如文件,查询数据,查询输 ...