Description

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

Input

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

Output

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

Sample Input

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

Sample Output

11

HINT

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

题解

就是求严格次小生成树

目前常见做法是先用Kurskal求出最小生成树

然后枚举不在树上的边,试着把它连到树上,找到形成的环上边权最大但不等于新加的边权的一条边,计算出(最小生成树权值+新加边边权-找到的最大)

对每条不在最小生成树上的边计算过之后再取min就是结果

而要求形成的环上边权最大但不等于新加的边权的一条边,我们就可以用树剖维护一个最大值和次大值,然后就可以轻松(mlog2n)得出答案了

我才不会说我边权转化成点权的时候没按dfn序结果调了一整天也没发现呢,哼

代码

//by 减维
#include<cstdio>
#include<iostream>
#include<cstring>
#include<queue>
#include<cstdlib>
#include<ctime>
#include<cmath>
#include<algorithm>
#define ll long long
#define ls l,mid,v<<1
#define rs mid+1,r,v<<1|1
#define getm mid=(l+r)>>1
using namespace std; struct us{
int x,y;
ll v;
}edg[]; struct edge{
int to,ne;
ll v;
}e[]; int n,m,num,ecnt,fa[],dep[],siz[],son[],val[];
int f[],dfn[],out[],head[],top[];
long long ans1,ans2,ans,ma[],ma2[];
bool pd[];
ll inf=1ll<<; bool cmp(const us&x,const us&y){return x.v<y.v;}
bool cm2(int x,int y){return x>y;} int find(int x)
{
if(x==fa[x])return x;
fa[x]=find(fa[x]);
return fa[x];
} void add(int x,int y,int z)
{
e[++ecnt].to=y;
e[ecnt].ne=head[x];
e[ecnt].v=z;
head[x]=ecnt;
} void df1(int x)
{
dep[x]=dep[f[x]]+;
siz[x]=;
for(int i=head[x];i;i=e[i].ne)
{
int dd=e[i].to;
if(dd==f[x])continue;
f[dd]=x;
df1(dd);
siz[x]+=siz[dd];
if(!son[x]||siz[son[x]]<siz[dd])
son[x]=dd;
}
} void dfs(int x,int tp)
{
top[x]=tp;
dfn[x]=++num;
if(son[x])dfs(son[x],tp);
for(int i=head[x];i;i=e[i].ne)
{
int dd=e[i].to;
if(dd==f[x])continue;
if(dd==son[x]){
val[dfn[dd]]=e[i].v;
continue;
}
dfs(dd,dd);
val[dfn[dd]]=e[i].v;
}
out[x]=num;
} void upda(int v)
{
int x[];
x[]=ma[v<<],x[]=ma[v<<|],x[]=ma2[v<<],x[]=ma2[v<<|];
sort(x+,x+,cm2);
ma[v]=x[];
ma2[v]=x[];
} void print(int l,int r,int v)
{
if(l==r){
printf("%d %d\n",ma[v],ma2[v]);
return ;
}
int mid;getm;
print(ls);
print(rs);
} void build(int l,int r,int v)
{
if(l==r){
ma[v]=val[l];
ma2[v]=-inf;
return ;
}
int mid;getm;
build(ls);
build(rs);
upda(v);
} ll ask(int l,int r,int v,int x,int y,int z)
{
if(r<x||y<l)return -inf;
if(x<=l&&r<=y){
if(ma[v]!=z)return ma[v];
return ma2[v];
}
int mid;getm;
return max(ask(ls,x,y,z),ask(rs,x,y,z));
} int main()
{
scanf("%d%d",&n,&m);
for(int i=;i<=m;++i)
scanf("%d%d%lld",&edg[i].x,&edg[i].y,&edg[i].v);
sort(edg+,edg+m+,cmp);
for(int i=;i<=n;++i)fa[i]=i;
int cntt=;
for(int i=;i<=m;++i)
{
int x=edg[i].x,y=edg[i].y;
int fx=find(x),fy=find(y);
if(fx!=fy){
cntt++;
pd[i]=;
fa[fx]=fa[fy];
ans1+=edg[i].v;
add(x,y,edg[i].v);
add(y,x,edg[i].v);
}
if(cntt==n-)break;
}
df1();
dfs(,);
build(,num,);
ans2=inf;
for(int i=;i<=m;++i)
if(!pd[i]){
int x=edg[i].x,y=edg[i].y;
ll tmp=-inf;
while(top[x]!=top[y])
{
if(dep[top[x]]>dep[top[y]]){
tmp=max(tmp,ask(,num,,dfn[top[x]],dfn[x],edg[i].v));
x=f[top[x]];
}else {
tmp=max(tmp,ask(,num,,dfn[top[y]],dfn[y],edg[i].v));
y=f[top[y]];
}
}
int lca=dep[x]<dep[y]?x:y,deper=dep[x]>dep[y]?x:y;
if(x!=y)tmp=max(tmp,ask(,num,,dfn[son[lca]],dfn[deper],edg[i].v));
ans2=min(ans2,ans1-tmp+edg[i].v);
}
printf("%lld",ans2);
}

还有一种类型题,本质上还是求次小生成树

【题目背景】

HJZ 买了一套新房子,他正在为新房子的装修发愁。

【题目描述】

土豪 HJZ 的新房子需要铺设水管网。 水管网一共有 n 个点,其中 1 号点是进水口,其余 n − 1 个点是出水口。

房子预留 了 m 根水管的空间,每根水管可以连接两个点,但要花费一定的代价。

现在 HJZ 想知道让所有出水口都直接或间接与进水口联通的最小代价。

由于装修 过程中可能会出现一些玄学事件,他还想知道代价最小的连接方案是否唯一。保证存在 一种连接方案。

【输入格式】

从文件 pipe.in 中读入数据。

第一行一个正整数 T 表示测试数据组数。

对于每一组测试数据:

• 第一行两个整数 n, m 分别表示水管网的点数和预留的管道数。

• 接下来 m 行,每行三个正整数 a, b, c 表示一条连接 a, b 的双向管道需要 c 的 代价。

【输出格式】 输出到文件 pipe.out 中。 对于每一组测试数据,分别输出两行。

第一行一个整数表示最小代价。 第二行一个字符串 Yes 或 No 表示连接方案是否唯一。

这道题当时考试的时候一时脑抽。。。就写出来了一个二分。。。留在这里当做纪念吧。。。

//by 减维
#include<cstdio>
#include<iostream>
#include<cstring>
#include<queue>
#include<cstdlib>
#include<ctime>
#include<cmath>
#include<map>
#include<bitset>
#include<algorithm>
#define ll long long
using namespace std; struct lin{
int fr,to,v;
}ed[]; struct edge{
int to,ne,v;
}e[]; struct node{
int v,pos;
}a[]; ll ans;
int t,n,m,num,tot,ecnt,head[],f[],fa[],lx[],rx[],son[],siz[],top[],dfn[],dep[];
bool fla,pd[]; bool cmp(const lin&x,const lin&y){return x.v<y.v;}
bool cm2(const node&x,const node&y){
if(x.v==y.v)return x.pos<y.pos;
return x.v<y.v;
} void init()
{
ecnt=num=tot=;
ans=;
fla=;
memset(f,,sizeof(f));
memset(pd,,sizeof(pd));
memset(lx,,sizeof(lx));
memset(rx,,sizeof(rx));
memset(dep,,sizeof(dep));
memset(son,,sizeof(son));
memset(siz,,sizeof(siz));
memset(dfn,,sizeof(dfn));
memset(top,,sizeof(top));
memset(head,,sizeof(head));
} void add(int x,int y,int z)
{
e[++ecnt].to=y;
e[ecnt].ne=head[x];
e[ecnt].v=z;
head[x]=ecnt;
} int find(int x)
{
if(x==fa[x])return x;
fa[x]=find(fa[x]);
return fa[x];
} void df1(int x)
{
siz[x]=;
dep[x]=dep[f[x]]+;
for(int i=head[x];i;i=e[i].ne)
{
int dd=e[i].to;
if(dd==f[x])continue ;
f[dd]=x;
df1(dd);
siz[x]+=siz[dd];
if(!son[x]||siz[son[x]]<siz[dd])
son[x]=dd;
}
} void dfs(int x,int tp)
{
top[x]=tp;
dfn[x]=++num;
if(son[x])dfs(son[x],tp);
for(int i=head[x];i;i=e[i].ne)
{
int dd=e[i].to;
if(dd==f[x]||dd==son[x])continue;
dfs(dd,dd);
}
} void df2(int x,int v)
{
a[++tot].v=v;
a[tot].pos=dfn[x];
for(int i=head[x];i;i=e[i].ne)
{
int dd=e[i].to;
if(dd==f[x])continue;
df2(dd,e[i].v);
}
} bool check(int x,int y,int v)
{
int l=lx[v],r=rx[v];
int mid;
while(l<=r){
mid=(l+r)>>;
if(a[mid].pos<=y&&a[mid].pos>=x)return ;
else if(a[mid].pos>y)r=mid-;
else l=mid+;
}
return ;
} int main()
{
freopen("pipe.in","r",stdin);
freopen("pipe.out","w",stdout);
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&m);
init();
for(int i=;i<=m;++i)scanf("%d%d%d",&ed[i].fr,&ed[i].to,&ed[i].v);
sort(ed+,ed+m+,cmp);
for(int i=;i<=n;++i)fa[i]=i;
int cnt=;
for(int i=,x,y,fx,fy;i<=m;++i)
{
x=ed[i].fr,y=ed[i].to;
fx=find(x),fy=find(y);
if(fx!=fy){
fa[fx]=fy;
cnt++;
ans+=(ll)ed[i].v;
add(x,y,ed[i].v);
add(y,x,ed[i].v);
pd[i]=;
}
if(cnt==n-)break;
}
printf("%lld\n",ans);
df1();
dfs(,);
df2(,);
sort(a+,a+tot+,cm2);
lx[a[].v]=;
for(int i=;i<=tot;++i)
if(a[i].v!=a[i-].v)lx[a[i].v]=i,rx[a[i-].v]=i-;
rx[a[tot].v]=tot;
for(int i=,x,y,lca,v,deper;i<=m;++i)
if(!pd[i])
{
x=ed[i].fr,y=ed[i].to,v=ed[i].v;
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]])swap(x,y);
if(check(dfn[top[x]],dfn[x],v)){
fla=;
break;
}
x=f[top[x]];
}
if(x!=y){
lca=dep[x]<dep[y]?x:y;
deper=lca==x?y:x;
if(check(dfn[son[lca]],dfn[deper],v))fla=;
}
if(fla)break;
}
if(fla)printf("No\n");
else printf("Yes\n");
}
return ;
}

【次小生成树】bzoj1977 [BeiJing2010组队]次小生成树 Tree的更多相关文章

  1. [BZOJ1977][BeiJing2010组队]次小生成树

    题解: 首先要证明一个东西 没有重边的图上 次小生成树由任何一颗最小生成树替换一条边 但是我不会证啊啊啊啊啊啊啊 然后就很简单了 枚举每一条边看看能不能变 但有一个特殊情况就是,他和环上的最大值相等, ...

  2. [bzoj1977][BeiJing2010组队]次小生成树 Tree——树上倍增+lca

    Brief Description 求一个无向图的严格次小生成树. Algorithm Design 考察最小生成树的生成过程.对于一个非树边而言,如果我们使用这一条非树边去替换原MST的路径上的最大 ...

  3. bzoj1977 [BeiJing2010组队]次小生成树 Tree

    和倍增法求lca差不多,维护每个点往上跳2^i步能到达的点,以及之间的边的最大值和次大值,先求出最小生成树,对于每个非树边枚举其端点在树上的路径的最大值,如果最大值和非树边权值一样则找次大值,然后维护 ...

  4. bzoj1977 [BeiJing2010组队]次小生成树 Tree——严格次小生成树

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1977 因为严格,所以要记录到 LCA 的一个次小值: 很快写好,然后改掉一堆错误后终于过了样 ...

  5. 【BZOJ1977】[BeiJing2010组队]次小生成树 Tree 最小生成树+倍增

    [BZOJ1977][BeiJing2010组队]次小生成树 Tree Description 小 C 最近学了很多最小生成树的算法,Prim 算法.Kurskal 算法.消圈算法等等. 正当小 C ...

  6. BZOJ 1977: [BeiJing2010组队]次小生成树 Tree( MST + 树链剖分 + RMQ )

    做一次MST, 枚举不在最小生成树上的每一条边(u,v), 然后加上这条边, 删掉(u,v)上的最大边(或严格次大边), 更新答案. 树链剖分然后ST维护最大值和严格次大值..倍增也是可以的... - ...

  7. 1977: [BeiJing2010组队]次小生成树 Tree

    1977: [BeiJing2010组队]次小生成树 Tree https://lydsy.com/JudgeOnline/problem.php?id=1977 题意: 求严格次小生成树,即边权和不 ...

  8. [BeiJing2010组队]次小生成树 Tree

    1977: [BeiJing2010组队]次小生成树 Tree Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 5168  Solved: 1668[S ...

  9. 【刷题】BZOJ 1977 [BeiJing2010组队]次小生成树 Tree

    Description 小 C 最近学了很多最小生成树的算法,Prim 算法.Kurskal 算法.消圈算法等等. 正当小 C 洋洋得意之时,小 P 又来泼小 C 冷水了.小 P 说,让小 C 求出一 ...

随机推荐

  1. 虚拟数据库_json_ajax

    html <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3 ...

  2. 【quickhybrid】API规划

    前言 当一切就绪后,就要开始进行API规划,这一块是整个Hybrid框架中非常重要的内容,毕竟对于前端页面来说,只会通过JS API来调用功能. 基本上,API调用起来是否方便简洁影响着整个体验. 这 ...

  3. 个人作业2:APP案例分析

    产品 产品名 网易云音乐 选择原因 除社交软件和浏览器以外,在手机里存在最久的也是使用次数最多的APP就是它了.不管换多少次手机和电脑,它始终在我的装机必备名单上. 调研与评测 第一次上手体验 第一次 ...

  4. 带你深度解析Maven

    一.What`s Maven? Maven是基于项目对象模型(POM project object model),可以通过一小段描述信息(配置)来管理项目的构建,报告和文档的软件项目管理工具,简单的说 ...

  5. .Net 异步方法, await async 使用

    最近朋友问起await  和 async第一次听说这个await ,就查了一下这个await使用在于 异步方法async 中,中文意思就是等待,经过一系列的百度参考简单的明白了这个东西的意思,  异步 ...

  6. 微信小程序——微信支付

    这个讲起来也就比较麻烦一点,因为需要的不仅仅是咱们代码上的技术,嘿嘿! 先整理一下思路.如果想做微信支付: 1.现有一个公司账户(非个人账户),并且实名认证过的. 2.微信号 必须开通微信支付功能. ...

  7. 微信小程序教学第四章第一节(含视频):小程序中级实战教程:详情-页面制作

    详情 - 页面制作 本文配套视频地址: https://v.qq.com/x/page/o0555o20xjd.html 开始前请把 ch4-1 分支中的 code/ 目录导入微信开发工具 这一章节中 ...

  8. 配置java项目的intellij idea的运行环境

    才疏学浅,只懂一点点前端的皮毛东西,对于项目运行环境的配置一无所知,今天简单记录一下! 前提:装好了jdk.maven.intellij idea. 1. file菜单->Open...打开从S ...

  9. Python 3.6.3 利用 Dlib 19.7 和 opencv 实现人脸68点定位 进行人脸识别

    0.引言 介绍利用Dlib官方给的人脸识别预测器"shape_predictor_68_face_landmarks.dat"进行68点标定,利用OpenCv进行图像化处理,在人脸 ...

  10. 关于《Head First Python》一书中print_lol()函数的思考

    关于<Head First Python>一书中print_lol()函数的思考 在<Head First Python>第一章中,讲述到Python处理复杂数据(以电影数据列 ...