博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
[模板] 严格次小生成树
阅读量:5142 次
发布时间:2019-06-13

本文共 4063 字,大约阅读时间需要 13 分钟。

题目描述

小C最近学了很多最小生成树的算法,Prim算法、Kurskal算法、消圈算法等等。正当小C洋洋得意之时,小P又来泼小C冷水了。小P说,让小C求出一个无向图的次小生成树,而且这个次小生成树还得是严格次小的,也就是说:如果最小生成树选择的边集是EM,严格次小生成树选择的边集是ES,那么需要满足:(value(e)表示边e的权值) \[\sum_{e∈EM}value(e)<\sum_{e∈ES}value(e)\]
这下小 C 蒙了,他找到了你,希望你帮他解决这个问题。

输入输出格式

输入格式:

第一行包含两个整数N 和M,表示无向图的点数与边数。 接下来 M行,每行 3个数x y z 表示,点 x 和点y之间有一条边,边的权值为z。

输出格式:

包含一行,仅一个数,表示严格次小生成树的边权和。(数据保证必定存在严格次小生成树)

输入输出样例

输入样例#1: 复制

5 6

1 2 1
1 3 2
2 4 3
3 5 4
3 4 3
4 5 6

输出样例#1: 复制

11

说明

数据中无向图无自环; 50% 的数据N≤2 000 M≤3 000; 80% 的数据N≤50 000, M≤100 000; 100% 的数据N≤100 000 M≤300 000 ,边权值非负且不超过 10^9 。

Solution

首先,我们得知道最小生成树和次小生成树只差一条边,我不会证明,想学的可以去网上找。严格次小生成树大致思路就是Kruskal+倍增LCA。

我们先用KrusKal求出最小生成树,标记一下哪些边用过,哪些边没用过,
我们肯定是用没用过的边去替换用过的边。
我们先看样例最小生成树的图片。
o_graph-1.png
接下来我们枚举每一条不在最小生成树的边,枚举第一条,如图:
o_graph-1.png
我们可以发现1,2,3,4成为了一个环,删掉任意一个环上的边都能形成生成树,我们容易得到新加入的这条边肯定是环上的最大值,(不然之前求的就不是最小生成树了)。我们只需换掉环上的最大的那条边就行了,但它要求严格次小,所以换的那条边不能等于当前这条边,所以我们还需维护一个严格次大值。那么我们怎么求解路径最大值和次大值呢?用倍增LCA维护一个最大值和次大值,就可以求解了。代码如下:

// luogu-judger-enable-o2#include
#include
#include
#include
using namespace std;typedef long long ll;ll last[100010],fa[201010],len,ans,dep[201010],f[201001][20],n,m;ll mx1[201000][20],mx2[201000][20],vis[301000],pai[10],anss=100000000;struct node{ ll to,next,w;}a[501010];struct kzj{ ll x,y,z; bool operator< (const kzj &c) const{return c.z>z;}}ff[301010];void add(ll a1,ll a2,ll a3){ a[++len].to=a2; a[len].w=a3; a[len].next=last[a1]; last[a1]=len;}ll find(ll x){ if(x==fa[x]) return x; return fa[x]=find(fa[x]);}void dfs(ll x,ll father){ for(ll i=last[x];i;i=a[i].next) { ll to=a[i].to; if(to==father) continue; dep[to]=dep[x]+1; f[to][0]=x; mx1[to][0]=a[i].w; dfs(to,x); }}bool cmp(ll a1,ll a2){return a1>a2;}void zuxian(){ for(ll j=1;j<=19;j++) for(ll i=1;i<=n;i++) { ll tot=0; f[i][j]=f[f[i][j-1]][j-1]; pai[++tot]=mx1[i][j-1]; pai[++tot]=mx2[i][j-1]; pai[++tot]=mx1[f[i][j-1]][j-1]; pai[++tot]=mx2[f[i][j-1]][j-1]; sort(pai+1,pai+tot,cmp); mx1[i][j]=pai[1]; for(ll k=2;k<=tot;k++) if(pai[k]!=pai[1]) {mx2[i][j]=pai[k];break;} }}void lca(ll x,ll y,ll kk){ ll ans1=0,ans2=0; if(dep[x]
ans1) { ans2=ans1,ans1=mx1[x][i]; if(mx2[x][i]>ans2) ans2=mx2[x][i]; } else if(mx1[x][i]>ans2) ans2=mx1[x][i]; x=f[x][i]; } if(x==y) { if(ans1!=ff[kk].z) anss=min(ff[kk].z-ans1,anss); else anss=min(ff[kk].z-ans2,anss); return; } for(ll i=19;i>=0;i--) { if(f[x][i]!=f[y][i]) { if(mx1[x][i]>ans1) { ans2=ans1,ans1=mx1[x][i]; if(mx2[x][i]>ans2) ans2=mx2[x][i]; } else if(mx1[x][i]>ans2) ans2=mx1[x][i]; if(mx1[y][i]>ans1) { ans2=ans1,ans1=mx1[y][i]; if(mx2[y][i]>ans2) ans2=mx2[y][i]; } else if(mx1[y][i]>ans2) ans2=mx1[y][i]; x=f[x][i]; y=f[y][i]; } } if(mx1[x][0]>ans1) { ans2=ans1,ans1=mx1[x][0]; if(mx2[x][0]>ans2) ans2=mx2[x][0]; } else if(mx1[x][0]>ans2) ans2=mx1[x][0]; if(mx1[y][0]>ans1) { ans2=ans1,ans1=mx1[y][0]; if(mx2[y][0]>ans2) ans2=mx2[y][0]; } else if(mx1[y][0]>ans2) ans2=mx1[y][0]; //cout<
<<' '<
<
>n>>m; for(ll i=1;i<=n;i++) fa[i]=i; for(ll i=1;i<=m;i++) scanf("%lld%lld%lld",&ff[i].x,&ff[i].y,&ff[i].z); sort(ff+1,ff+1+m); for(ll i=1;i<=m;i++) { ll x=ff[i].x,y=ff[i].y; ll f1=find(x),f2=find(y); if(f1!=f2) { vis[i]=1; cnt++; fa[f1]=f2; ans+=ff[i].z; add(x,y,ff[i].z); add(y,x,ff[i].z); } if(cnt==n-1) break; } dep[1]=1; dfs(1,0); zuxian(); for(ll i=1;i<=m;i++) { if(vis[i]) continue; ll x=ff[i].x,y=ff[i].y; lca(x,y,i); } cout<

博主蒟蒻,可以随意转载,但必须附上原文链接。

转载于:https://www.cnblogs.com/kzj-pwq/p/9493568.html

你可能感兴趣的文章
h5唤起app
查看>>
SQL Server 2008 /SQL Server 2008 R2 配置数据库邮件
查看>>
[转]vs2010编译金山代码
查看>>
数学图形之Boy surface
查看>>
处理程序“PageHandlerFactory-Integrated”在其模块列表中有一个错误模块“Manag
查看>>
01: socket模块
查看>>
mysql触发器
查看>>
淌淌淌
查看>>
MySQL-定时任务
查看>>
web页面实现指定区域打印功能
查看>>
使用PHP拆分中文字符串的方法(收藏) 小节
查看>>
android系统权限的管理
查看>>
win10每次开机都显示“你的硬件设置已更改,请重启电脑……”的解决办法
查看>>
因Window服务器自动更新并重启导致WebSphere服务停止服务故障一例
查看>>
如何开启safari的调试
查看>>
js深拷贝和浅拷贝
查看>>
node.js 基础学习笔记1
查看>>
如何在linux系统中设置静态ip地址
查看>>
二分查找法,折半查找原理
查看>>
DP简单问题联系--最长递增子序列+最长公共子序列等
查看>>