Luogu P4234 最小差值生成树
题意
给定一个 \(n\) 个点 \(m\) 条边的有权无向图,求出原图的一棵生成树使得该树上最大边权与最小边权的差值最小。
\(\texttt{Data Range:}1\leq n\leq 5\times 10^4,1\leq m\leq 2\times 10^5,1\leq w\leq 5\times 10^4\)
题解
这位傻逼把 \(n\) 个节点的树所含有的边的数目认为是 \(n\),还在出现环的时候立即统计答案(可能生成树都没构建出来),导致 \(\texttt{WA}\) 了一发又一发,请大家不要学他。
直接进入正题。
考虑先将边权从小到大排序,然后一条一条的考虑。
如果这条边加入当前的图中不会成环的话,那么直接加进来即可。
如果成了环的话,考虑在环上删去边权最小的边。
为什么这么做是正确的呢?
假设我们现在考虑的是第 \(k\) 条边,连接 \(u_i\) 和 \(v_i\),边权为 \(w_i\),讨论一下当前图中边权最小的边的位置。
当这条边的位置在环上的时候,由于新图的边权最小值改变了,所以需要更新答案。
当这条边的位置不在环上的时候,对答案无影响。
所以我们每一次需要更新答案的时候我们都进行了更新,自然答案就是对的了。
加入完这条边之后,如果当前的图是原图的一棵生成树的话那么更新一下答案就好了。
注意到整个过程涉及到删边和连边操作,所以采用 \(\texttt{LCT}\) 来维护生成树。
更新答案时,由于要找到最小边权的边的位置,并且每一次加完边之后最小边权的边的位置单调不降(因为排过序了),打标记+移动指针就可以了。
代码
#include<bits/stdc++.h>
using namespace std;
typedef int ll;
typedef long long int li;
const ll MAXN=3e5+51;
struct Edge{
ll from,to,dist;
inline bool operator <(const Edge &rhs)const
{
return dist<rhs.dist;
}
};
ll n,m,ptr=1,cur,x,y,offset=60000,res;
Edge ed[MAXN];
ll ffa[MAXN],vis[MAXN];
inline ll read()
{
register ll num=0,neg=1;
register char ch=getchar();
while(!isdigit(ch)&&ch!='-')
{
ch=getchar();
}
if(ch=='-')
{
neg=-1;
ch=getchar();
}
while(isdigit(ch))
{
num=(num<<3)+(num<<1)+(ch-'0');
ch=getchar();
}
return num*neg;
}
inline ll find(ll x)
{
return x==ffa[x]?x:ffa[x]=find(ffa[x]);
}
namespace LCT{
struct Node{
ll fa,mn,val,rv,sz;
ll ch[2];
};
struct LinkCutTree{
Node nd[MAXN];
ll st[MAXN];
#define ls nd[x].ch[0]
#define rs nd[x].ch[1]
inline bool nroot(ll x)
{
return nd[nd[x].fa].ch[0]==x||nd[nd[x].fa].ch[1]==x;
}
inline ll get(ll x,ll y)
{
return nd[x].val<nd[y].val?x:y;
}
inline void update(ll x)
{
nd[x].mn=get(x,get(nd[ls].mn,nd[rs].mn));
}
inline void reverse(ll x)
{
swap(ls,rs),nd[x].rv^=1;
}
inline void spread(ll x)
{
if(nd[x].rv)
{
ls?reverse(ls):(void)(1),rs?reverse(rs):(void)(1);
nd[x].rv=0;
}
}
inline void rotate(ll x)
{
ll fa=nd[x].fa,gfa=nd[fa].fa;
ll dir=nd[fa].ch[1]==x,son=nd[x].ch[!dir];
if(nroot(fa))
{
nd[gfa].ch[nd[gfa].ch[1]==fa]=x;
}
nd[x].ch[!dir]=fa,nd[fa].ch[dir]=son;
if(son)
{
nd[son].fa=fa;
}
nd[fa].fa=x,nd[x].fa=gfa,update(fa);
}
inline void splay(ll x)
{
ll fa=x,gfa,cur=0;
st[++cur]=fa;
while(nroot(fa))
{
st[++cur]=fa=nd[fa].fa;
}
while(cur)
{
spread(st[cur--]);
}
while(nroot(x))
{
fa=nd[x].fa,gfa=nd[fa].fa;
if(nroot(fa))
{
rotate((nd[fa].ch[0]==x)^(nd[gfa].ch[0]==fa)?x:fa);
}
rotate(x);
}
update(x);
}
inline void access(ll x)
{
for(register int i=0;x;x=nd[i=x].fa)
{
splay(x),rs=i,update(x);
}
}
inline void makeRoot(ll x)
{
access(x),splay(x),reverse(x);
}
inline void split(ll x,ll y)
{
makeRoot(x),access(y),splay(y);
}
inline void link(ll x)
{
makeRoot(ed[x].from);
nd[nd[ed[x].from].fa=x+offset].fa=ed[x].to;
nd[x+offset].val=ed[x].dist,update(x);
}
inline void cut(ll x)
{
access(ed[x-offset].from),splay(x);
ls=rs=nd[ls].fa=nd[rs].fa=0,update(x);
}
#undef ls
#undef rs
};
}
LCT::LinkCutTree lct;
int main()
{
n=read(),m=read();
for(register int i=1;i<=m;i++)
{
ed[i].from=read(),ed[i].to=read(),ed[i].dist=read();
}
for(register int i=0;i<=n;i++)
{
ffa[i]=i,lct.nd[i].val=0x7fffffff;
}
sort(ed+1,ed+m+1);
for(register int i=1;i<=m;i++)
{
if(find(x=ed[i].from)!=find(y=ed[i].to))
{
vis[i]=1,lct.link(i),cur++,ffa[ffa[y]]=ffa[x];
if(cur==n-1)
{
res=ed[i].dist-ed[ptr].dist;
}
}
else
{
if(x==y)
{
continue;
}
lct.split(x,y),vis[lct.nd[y].mn-offset]=0;
lct.cut(lct.nd[y].mn),lct.link(i),vis[i]=1;
while(!vis[ptr])
{
ptr++;
}
if(cur==n-1)
{
res=min(res,ed[i].dist-ed[ptr].dist);
}
}
}
printf("%d\n",res);
}
Luogu P4234 最小差值生成树的更多相关文章
- P4234 最小差值生成树
题目 P4234 最小差值生成树 做法 和这题解法差不多,稍微变了一点,还不懂就直接看代码吧 \(update(2019.2):\)还是具体说一下吧,排序,直接加入,到了成环情况下,显然我们要把此边代 ...
- 【Luogu】P4234最小差值生成树(LCT)
题目链接 能把LCT打得每个函数都恰有一个错误也是挺令我惊讶的. 本题使用LCT维护生成树,具体做法是对原图中的每个边建一个点,然后连边的时候相当于是将边的起点跟“边”这个点连起来,边的终点也跟它连起 ...
- 【刷题】洛谷 P4234 最小差值生成树
题目描述 给定一个标号为从 \(1\) 到 \(n\) 的.有 \(m\) 条边的无向图,求边权最大值与最小值的差值最小的生成树. 输入输出格式 输入格式: 第一行两个数 \(n, m\) ,表示图的 ...
- 洛谷P4234 最小差值生成树(lct动态维护最小生成树)
题目描述 给定一个标号为从 11 到 nn 的.有 mm 条边的无向图,求边权最大值与最小值的差值最小的生成树. 输入输出格式 输入格式: 第一行两个数 n, mn,m ,表示图的点和边的数量. ...
- P4234 最小差值生成树 LCT维护边权
\(\color{#0066ff}{ 题目描述 }\) 给定一个标号为从 \(1\) 到 \(n\) 的.有 \(m\) 条边的无向图,求边权最大值与最小值的差值最小的生成树. \(\color{#0 ...
- [洛谷P4234] 最小差值生成树
题目类型:\(LCT\)动态维护最小生成树 传送门:>Here< 题意:求一棵生成树,其最大边权减最小边权最小 解题思路 和魔法森林非常像.先对所有边进行排序,每次加边的时候删除环上的最小 ...
- 洛谷 P4234 最小差值生成树(LCT)
题面 luogu 题解 LCT 动态树Link-cut tree(LCT)总结 考虑先按边权排序,从小到大加边 如果构成一颗树了,就更新答案 当加入一条边,会形成环. 贪心地想,我们要最大边权-最小边 ...
- Luogu 4234 最小差值生成树 - LCT 维护链信息
Solution 将边从小到大排序, 添新边$(u, v)$时 若$u,v$不连通则直接添, 若连通则 把链上最小的边去掉 再添边. 若已经加入了 $N - 1$条边则更新答案. Code #incl ...
- 洛谷P4234 最小差值生成树(LCT,生成树)
洛谷题目传送门 和魔法森林有点像,都是动态维护最小生成树(可参考一下Blog的LCT总结相关部分) 至于从小到大还是从大到小当然无所谓啦,我是从小到大排序,每次枚举边,还没连通就连,已连通就替换环上最 ...
随机推荐
- 【MindSpore】Docker上成功使用MindSpore1.0.0的GPU版本
本文是在宿主机Ubuntu16.04上安装Docker(nvidia-docker),并成功进行MindSpore1.0.0的GPU训练: Ubuntu 16.04 Docker Nvidia-doc ...
- 创建Vue项目及封装axios
1. 始vue化项目 https://www.cnblogs.com/xiaonq/p/11027880.html vue init webpack deaxios # 使用脚手架创建项目 deaxi ...
- newifi3-D2 openwrt挂载u盘扩容/overlay
格式化U盘 1.openwrt安装插件 opkg install fdisk swap-utils kmod-usb-storage kmod-fs-ext4 e2fsprogs kmod-usb-o ...
- 什么是64位和32位internet explorer
什么是64位和32位internet explorer 如果您使用 64 位版本的 Internet Explorer 时,您会遇到问题,请尝试使用 32 位版本的 Internet Explorer ...
- 022 01 Android 零基础入门 01 Java基础语法 03 Java运算符 02 算术运算符
022 01 Android 零基础入门 01 Java基础语法 03 Java运算符 02 算术运算符 本文知识点:Java中的算术运算符 算术运算符介绍 算术运算符代码示例 注意字符串连接问题和整 ...
- OpenCV计算机视觉学习(2)——图像算术运算 & 掩膜mask操作(数值计算,图像融合,边界填充)
在OpenCV中我们经常会遇到一个名字:Mask(掩膜).很多函数都使用到它,那么这个Mask到底是什么呢,下面我们从图像基本运算开始,一步一步学习掩膜. 1,图像算术运算 图像的算术运算有很多种,比 ...
- C1853 编译器错误:fatal error C1853: 'pjtname.pch' precompiled header file is from a previous
转载:https://www.cnblogs.com/emanlee/archive/2010/10/16/1852998.html 用VC++ 2008 编写C语言程序,编译出现错误: 预编译头文件 ...
- 图像sensor的bitdepth
参考来源:https://blog.csdn.net/yuejisuo1948/article/details/83617359 bitdepth目前个人理解是sensor像素上表示颜色的范围,也可说 ...
- shell-脚本的执行
1. shell脚本的执行 当shell脚本以非交互的方式运行时,它会先查找环境变量ENV,该变量指定了一个环境文件(通常是.bashrc),然后从该环境变量文件开始执行,当读取了ENV文件后,she ...
- Shell Scripting 笔记
Shell Scripting Tutorial Variables in the Bourne shell do not have to be declared, as they do in lan ...