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时, ...
随机推荐
- 快照方式备份MySQL数据库及举例
快照方式备份MySQL数据库及举例 作者: 红豆殺 日期: 2011 年 03 月 17 日发表评论7条评论查看评论 一.创建逻辑卷 依照如下连接的文档创建一个逻辑卷 http://www.178 ...
- 【bzoj4551】【NOIP2016模拟7.11】树
题目 在2016年,佳媛姐姐刚刚学习了树,非常开心.现在他想解决这样一个问题:给定一颗有根树(根为1),有以下 两种操作:1. 标记操作:对某个结点打上标记(在最开始,只有结点1有标记,其他结点均无标 ...
- HTML和CSS实现的透明登录框效果
实现代码 HTML部分 <!DOCTYPE html> <html lang="en"> <head> <meta charset=&qu ...
- mysql 语句积累
show tables; 显示表 DROP TABLE IF EXISTS emp;删除表
- mysql PRIMARY KEY约束 语法
mysql PRIMARY KEY约束 语法 作用:PRIMARY KEY 约束唯一标识数据库表中的每条记录. 环形直线电机 说明:主键必须包含唯一的值.主键列不能包含 NULL 值.每个表都应该有一 ...
- CSS中的 , > + ~
1.群组选择器(',') /* 表示既h1,又h2 */ h1, h2 { color: red; } 2.后代选择器(空格) /* 表示 h1 下面的所有 span 元素,不管是否以 h1 为直接父 ...
- HOJ 2315 Time(模拟)
Description Kim是一个掌控时间的大师.不同于一般人,他习惯使用秒来计算时间.如果你问他现在是几点,他会告诉你现在是今天的xxxx秒.Mik想要考考Kim.他想知道从某一天的00:00:0 ...
- [USACO07OPEN]Dining 题解
前言 如果有人不会网络流,那么安利一下我网络最大流Dinic的博客 关于网络流,我多久没有碰这个算法了... 这是一道网络流好题. 题解 这道题目难点主要是构图. 这道题的构图一开始很容易想到建一个超 ...
- Python_019(六星级别之反射方法)
1.反射 1)神赐给你的内置函数 : a: getattr(命名空间,'函数名') == 命名空间.属性名; 这里的命名空间指的是对象或者类; b: getattr四个应用场景: 1)类名.名字 &l ...
- MySql中报错:java.sql.SQLException: Incorrect string value: '\xF0\x9F\x90\xBB' for column
问题描述: java.sql.SQLException: Incorrect string value: '\xF0\x9F\x90\xBB' for column 'nickName' at row ...