洛谷.4180.[模板]次小生成树Tree(Kruskal LCA 倍增)
构建完MST后,枚举非树边(u,v,w),在树上u->v的路径中找一条权值最大的边(权为maxn),替换掉它
这样在 w=maxn 时显然不能满足严格次小。但是这个w可以替换掉树上严格小于maxn的次大边
用倍增维护MST上路径的最大值、次大值,每条非树边的查询复杂度就为O(logn)
ps:1.倍增更新次大值时,未必是从最大值转移,要先赋值较大的次大值,再与较小的那个最大值比较。
2.maxn!=w时,是可以从maxn更新的(不能更新就是上面情况啊)
倍增处理部分我还是在dfs里写吧 md改了一晚上
//648ms 31.61MB
#include <cstdio>
#include <cctype>
#include <algorithm>
//#define gc() getchar()
#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
typedef long long LL;
const int N=1e5+5,M=3e5+5,D=18,MAXIN=2e6;
int n,m,F[N],Enum,H[N],to[N<<1],nxt[N<<1],val[N<<1],dep[N],fa[N][D],maxn[N][D],s_maxn[N][D];
bool ist[M];
char IN[MAXIN],*SS=IN,*TT=IN;
struct Edge
{
int fr,to,val;
bool operator <(const Edge &a)const{
return val<a.val;
}
}e[M];
inline int read()
{
int now=0;register char c=gc();
for(;!isdigit(c);c=gc());
for(;isdigit(c);now=now*10+c-'0',c=gc());
return now;
}
inline void AddEdge_T(int u,int v,int w){
to[++Enum]=v, nxt[Enum]=H[u], val[Enum]=w, H[u]=Enum;
to[++Enum]=u, nxt[Enum]=H[v], val[Enum]=w, H[v]=Enum;
}
int Find(int x){
return x==F[x]?x:F[x]=Find(F[x]);
}
LL Kruskal()
{
LL res=0ll;
std::sort(e+1,e+1+m);
for(int i=1; i<=n; ++i) F[i]=i;
for(int u,v,k=0,i=1; i<=m; ++i)
{
u=Find(e[i].fr), v=Find(e[i].to);
if(u!=v)
{
ist[i]=1, F[u]=v, res+=e[i].val;
AddEdge_T(e[i].fr,e[i].to,e[i].val);//别拿u,v建边
if(++k==n-1) break;
}
}
return res;
}
void pre_DFS(int x)
{
for(int i=1; i<D && (1<<i)<=dep[x]; ++i)
{
fa[x][i]=fa[fa[x][i-1]][i-1];
maxn[x][i]=std::max(maxn[fa[x][i-1]][i-1],maxn[x][i-1]);
s_maxn[x][i]=std::max(s_maxn[fa[x][i-1]][i-1],s_maxn[x][i-1]);
if(maxn[fa[x][i-1]][i-1]>maxn[x][i-1])
s_maxn[x][i]=std::max(s_maxn[x][i],maxn[x][i-1]);
else if(maxn[fa[x][i-1]][i-1]<maxn[x][i-1])//不要直接else
s_maxn[x][i]=std::max(s_maxn[x][i],maxn[fa[x][i-1]][i-1]);
}
for(int v,i=H[x]; i; i=nxt[i])
if((v=to[i])!=fa[x][0])
fa[v][0]=x, dep[v]=dep[x]+1, maxn[v][0]=val[i], pre_DFS(v);
}
//void Init()//这步先处理i!
//{
// for(int i=1; i<D; ++i)
// for(int x=2; x<=n; ++x)
// {
// fa[x][i]=fa[fa[x][i-1]][i-1];
// maxn[x][i]=std::max(maxn[fa[x][i-1]][i-1],maxn[x][i-1]);
// s_maxn[x][i]=std::max(s_maxn[fa[x][i-1]][i-1],s_maxn[x][i-1]);
// if(maxn[fa[x][i-1]][i-1]>maxn[x][i-1])
// s_maxn[x][i]=std::max(s_maxn[x][i],maxn[x][i-1]);
// else if(maxn[fa[x][i-1]][i-1]<maxn[x][i-1])//不要直接else
// s_maxn[x][i]=std::max(s_maxn[x][i],maxn[fa[x][i-1]][i-1]);
// }
//}
int Query(int u,int lca,int w)
{
int res=0;
for(int i=D-1; i>=0; --i)
if(dep[fa[u][i]]>=dep[lca])
{
if(maxn[u][i]!=w) res=std::max(res,maxn[u][i]);
else res=std::max(res,s_maxn[u][i]);
u=fa[u][i];
}
return res;
}
int LCA(int u,int v)
{
if(dep[u]<dep[v]) std::swap(u,v);
int t=dep[u]-dep[v];
for(int i=D-1; i>=0; --i)
if(dep[fa[u][i]]>=dep[v]) u=fa[u][i];
// if(t&(1<<i)) u=fa[u][i];
if(u==v) return u;
for(int i=D-1; i>=0; --i)
if(fa[u][i]!=fa[v][i])
u=fa[u][i], v=fa[v][i];
return fa[u][0];
}
int main()
{
n=read(),m=read();
for(int i=1; i<=m; ++i) e[i].fr=read(),e[i].to=read(),e[i].val=read();
LL tot=Kruskal(),res=e[m].val+tot;
dep[1]=1, pre_DFS(1);//, Init();
for(int u,v,w,lmax,rmax,i=1; i<=m; ++i)
if(!ist[i])
{
u=e[i].fr, v=e[i].to, w=LCA(u,v);
lmax=Query(u,w,e[i].val), rmax=Query(v,w,e[i].val);
res=std::min(res,tot+e[i].val-std::max(lmax,rmax));
}
printf("%lld",res);
return 0;
}
洛谷.4180.[模板]次小生成树Tree(Kruskal LCA 倍增)的更多相关文章
- BZOJ1977或洛谷4180 [BJWC2010]次小生成树
一道LCA+生成树 BZOJ原题链接 洛谷原题链接 细节挺多,我调了半天..累炸.. 回到正题,我们先求出随便一棵最小生成树(设边权和为\(s\)),然后扫描剩下所有边,设扫到的边的两端点为\(x,y ...
- 洛谷P4180 [BJWC2010]次小生成树(最小生成树,LCT,主席树,倍增LCA,倍增,树链剖分)
洛谷题目传送门 %%%TPLY巨佬和ysner巨佬%%% 他们的题解 思路分析 具体思路都在各位巨佬的题解中.这题做法挺多的,我就不对每个都详细讲了,泛泛而谈吧. 大多数算法都要用kruskal把最小 ...
- bzoj 1977 洛谷P4180 严格次小生成树
Description: 给定一张N个节点M条边的无向图,求该图的严格次小生成树.设最小生成树边权之和为sum,那么严格次小生成树就是边权之和大于sum的最小的一个 Input: 第一行包含两个整数N ...
- P4180 严格次小生成树[BJWC2010] Kruskal,倍增
题目链接\(Click\) \(Here\). 题意就是要求一个图的严格次小生成树.以前被题面吓到了没敢做,写了一下发现并不难. 既然要考虑次小我们就先考虑最小.可以感性理解到一定有一种次小生成树,可 ...
- BZOJ 1977: [BeiJing2010组队]次小生成树 Tree( MST + 树链剖分 + RMQ )
做一次MST, 枚举不在最小生成树上的每一条边(u,v), 然后加上这条边, 删掉(u,v)上的最大边(或严格次大边), 更新答案. 树链剖分然后ST维护最大值和严格次大值..倍增也是可以的... - ...
- 洛谷P3373 [模板]线段树 2(区间增减.乘 区间求和)
To 洛谷.3373 [模板]线段树2 题目描述 如题,已知一个数列,你需要进行下面两种操作: 1.将某区间每一个数加上x 2.将某区间每一个数乘上x 3.求出某区间每一个数的和 输入输出格式 输入格 ...
- 洛谷P2633/bzoj2588 Count on a tree (主席树)
洛谷P2633/bzoj2588 Count on a tree 题目描述 给定一棵N个节点的树,每个点有一个权值,对于M个询问(u,v,k),你需要回答u xor lastans和v这两个节点间第K ...
- 1977: [BeiJing2010组队]次小生成树 Tree
1977: [BeiJing2010组队]次小生成树 Tree https://lydsy.com/JudgeOnline/problem.php?id=1977 题意: 求严格次小生成树,即边权和不 ...
- 【BZOJ1977】[BeiJing2010组队]次小生成树 Tree 最小生成树+倍增
[BZOJ1977][BeiJing2010组队]次小生成树 Tree Description 小 C 最近学了很多最小生成树的算法,Prim 算法.Kurskal 算法.消圈算法等等. 正当小 C ...
随机推荐
- python3 三元表达式,列表解析
python3 三元表达式,列表解析 三元表达式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 x=2 y=3 if x > y ...
- Device Tree常用方法解析【转】
转自:https://blog.csdn.net/airk000/article/details/21345159 Device Tree常用方法解析 Device Tree在Linux内核驱动中的使 ...
- Linux内核驱动基础(一)常用宏定义【转】
转自:http://blog.csdn.net/tommy_wxie/article/details/9427081 一: __init和__initdata : __exit和__exitdata ...
- diff 命令用法--如何打补丁【原创--学习笔记】
diff 命令用法 1.”-u”:表示在比较结果中输出上下文中一些相同的行,这有利于人工定位 2.“-r“:表示递归比较各个子目录下的文件 3.“-N“:将不存在的文件当作空文件 4.“-w“:忽略对 ...
- R-TREE
原文地址:http://blog.csdn.net/sunmenggmail/article/details/8122743 1984年,加州大学伯克利分校的Guttman发表了一篇题为“R-tree ...
- 解决maven编译Java中的使用了未经检查或不安全的操作
eclipse编译器找到右侧黄色点击可以定位到对应代码块位置 解决方法:鼠标悬浮在上方点击add即可 @SuppressWarnings("unchecked");给出的解决方案 ...
- Laravel API 限速异常 HTTPException Too Many Attemps
最近线上 Laravel 错误日志发现了一个异常 HTTPException Too Many Attemps 而我一个同事在本地开发的过程中也频繁遇到这个异常.测试环境:Laravel 5.5. 网 ...
- 怎么用pr(Premiere)给视频添加水印
自己辛辛苦苦剪切的视频,不添加水印,别人肯定会搬走的,作为对视频付出时间的你肯定不想看见这样的事情,下面一起来看看怎么用pr给视频添加水印吧 工具/原料 水印 pr 方法/步骤 首先打 ...
- django 如何动态使用Q查询函数
这个Q和F用得少, 需要时,总是独立的存在于那时,显得有些突兀, 这次想将filter,order,Q集合在一起, 查询了很多资料,还是有一些困难, 但即可以将Q查询比较优雅的动态生成. 比如: # ...
- 响应式web布局设计实战总结教程
响应性web设计的理念是:页面的设计与开发应当根据用户行为与设备环境(包括系统平台,屏幕尺寸,屏幕定向等)进行相应的响应及调整.具体的实践方式由多方面组成,包括弹性网格和布局,图片,css Media ...