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序结果调了一整天也没发现呢,哼

代码

  1. //by 减维
  2. #include<cstdio>
  3. #include<iostream>
  4. #include<cstring>
  5. #include<queue>
  6. #include<cstdlib>
  7. #include<ctime>
  8. #include<cmath>
  9. #include<algorithm>
  10. #define ll long long
  11. #define ls l,mid,v<<1
  12. #define rs mid+1,r,v<<1|1
  13. #define getm mid=(l+r)>>1
  14. using namespace std;
  15.  
  16. struct us{
  17. int x,y;
  18. ll v;
  19. }edg[];
  20.  
  21. struct edge{
  22. int to,ne;
  23. ll v;
  24. }e[];
  25.  
  26. int n,m,num,ecnt,fa[],dep[],siz[],son[],val[];
  27. int f[],dfn[],out[],head[],top[];
  28. long long ans1,ans2,ans,ma[],ma2[];
  29. bool pd[];
  30. ll inf=1ll<<;
  31.  
  32. bool cmp(const us&x,const us&y){return x.v<y.v;}
  33. bool cm2(int x,int y){return x>y;}
  34.  
  35. int find(int x)
  36. {
  37. if(x==fa[x])return x;
  38. fa[x]=find(fa[x]);
  39. return fa[x];
  40. }
  41.  
  42. void add(int x,int y,int z)
  43. {
  44. e[++ecnt].to=y;
  45. e[ecnt].ne=head[x];
  46. e[ecnt].v=z;
  47. head[x]=ecnt;
  48. }
  49.  
  50. void df1(int x)
  51. {
  52. dep[x]=dep[f[x]]+;
  53. siz[x]=;
  54. for(int i=head[x];i;i=e[i].ne)
  55. {
  56. int dd=e[i].to;
  57. if(dd==f[x])continue;
  58. f[dd]=x;
  59. df1(dd);
  60. siz[x]+=siz[dd];
  61. if(!son[x]||siz[son[x]]<siz[dd])
  62. son[x]=dd;
  63. }
  64. }
  65.  
  66. void dfs(int x,int tp)
  67. {
  68. top[x]=tp;
  69. dfn[x]=++num;
  70. if(son[x])dfs(son[x],tp);
  71. for(int i=head[x];i;i=e[i].ne)
  72. {
  73. int dd=e[i].to;
  74. if(dd==f[x])continue;
  75. if(dd==son[x]){
  76. val[dfn[dd]]=e[i].v;
  77. continue;
  78. }
  79. dfs(dd,dd);
  80. val[dfn[dd]]=e[i].v;
  81. }
  82. out[x]=num;
  83. }
  84.  
  85. void upda(int v)
  86. {
  87. int x[];
  88. x[]=ma[v<<],x[]=ma[v<<|],x[]=ma2[v<<],x[]=ma2[v<<|];
  89. sort(x+,x+,cm2);
  90. ma[v]=x[];
  91. ma2[v]=x[];
  92. }
  93.  
  94. void print(int l,int r,int v)
  95. {
  96. if(l==r){
  97. printf("%d %d\n",ma[v],ma2[v]);
  98. return ;
  99. }
  100. int mid;getm;
  101. print(ls);
  102. print(rs);
  103. }
  104.  
  105. void build(int l,int r,int v)
  106. {
  107. if(l==r){
  108. ma[v]=val[l];
  109. ma2[v]=-inf;
  110. return ;
  111. }
  112. int mid;getm;
  113. build(ls);
  114. build(rs);
  115. upda(v);
  116. }
  117.  
  118. ll ask(int l,int r,int v,int x,int y,int z)
  119. {
  120. if(r<x||y<l)return -inf;
  121. if(x<=l&&r<=y){
  122. if(ma[v]!=z)return ma[v];
  123. return ma2[v];
  124. }
  125. int mid;getm;
  126. return max(ask(ls,x,y,z),ask(rs,x,y,z));
  127. }
  128.  
  129. int main()
  130. {
  131. scanf("%d%d",&n,&m);
  132. for(int i=;i<=m;++i)
  133. scanf("%d%d%lld",&edg[i].x,&edg[i].y,&edg[i].v);
  134. sort(edg+,edg+m+,cmp);
  135. for(int i=;i<=n;++i)fa[i]=i;
  136. int cntt=;
  137. for(int i=;i<=m;++i)
  138. {
  139. int x=edg[i].x,y=edg[i].y;
  140. int fx=find(x),fy=find(y);
  141. if(fx!=fy){
  142. cntt++;
  143. pd[i]=;
  144. fa[fx]=fa[fy];
  145. ans1+=edg[i].v;
  146. add(x,y,edg[i].v);
  147. add(y,x,edg[i].v);
  148. }
  149. if(cntt==n-)break;
  150. }
  151. df1();
  152. dfs(,);
  153. build(,num,);
  154. ans2=inf;
  155. for(int i=;i<=m;++i)
  156. if(!pd[i]){
  157. int x=edg[i].x,y=edg[i].y;
  158. ll tmp=-inf;
  159. while(top[x]!=top[y])
  160. {
  161. if(dep[top[x]]>dep[top[y]]){
  162. tmp=max(tmp,ask(,num,,dfn[top[x]],dfn[x],edg[i].v));
  163. x=f[top[x]];
  164. }else {
  165. tmp=max(tmp,ask(,num,,dfn[top[y]],dfn[y],edg[i].v));
  166. y=f[top[y]];
  167. }
  168. }
  169. int lca=dep[x]<dep[y]?x:y,deper=dep[x]>dep[y]?x:y;
  170. if(x!=y)tmp=max(tmp,ask(,num,,dfn[son[lca]],dfn[deper],edg[i].v));
  171. ans2=min(ans2,ans1-tmp+edg[i].v);
  172. }
  173. printf("%lld",ans2);
  174. }

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

【题目背景】

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

【题目描述】

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

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

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

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

【输入格式】

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

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

对于每一组测试数据:

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

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

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

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

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

  1. //by 减维
  2. #include<cstdio>
  3. #include<iostream>
  4. #include<cstring>
  5. #include<queue>
  6. #include<cstdlib>
  7. #include<ctime>
  8. #include<cmath>
  9. #include<map>
  10. #include<bitset>
  11. #include<algorithm>
  12. #define ll long long
  13. using namespace std;
  14.  
  15. struct lin{
  16. int fr,to,v;
  17. }ed[];
  18.  
  19. struct edge{
  20. int to,ne,v;
  21. }e[];
  22.  
  23. struct node{
  24. int v,pos;
  25. }a[];
  26.  
  27. ll ans;
  28. int t,n,m,num,tot,ecnt,head[],f[],fa[],lx[],rx[],son[],siz[],top[],dfn[],dep[];
  29. bool fla,pd[];
  30.  
  31. bool cmp(const lin&x,const lin&y){return x.v<y.v;}
  32. bool cm2(const node&x,const node&y){
  33. if(x.v==y.v)return x.pos<y.pos;
  34. return x.v<y.v;
  35. }
  36.  
  37. void init()
  38. {
  39. ecnt=num=tot=;
  40. ans=;
  41. fla=;
  42. memset(f,,sizeof(f));
  43. memset(pd,,sizeof(pd));
  44. memset(lx,,sizeof(lx));
  45. memset(rx,,sizeof(rx));
  46. memset(dep,,sizeof(dep));
  47. memset(son,,sizeof(son));
  48. memset(siz,,sizeof(siz));
  49. memset(dfn,,sizeof(dfn));
  50. memset(top,,sizeof(top));
  51. memset(head,,sizeof(head));
  52. }
  53.  
  54. void add(int x,int y,int z)
  55. {
  56. e[++ecnt].to=y;
  57. e[ecnt].ne=head[x];
  58. e[ecnt].v=z;
  59. head[x]=ecnt;
  60. }
  61.  
  62. int find(int x)
  63. {
  64. if(x==fa[x])return x;
  65. fa[x]=find(fa[x]);
  66. return fa[x];
  67. }
  68.  
  69. void df1(int x)
  70. {
  71. siz[x]=;
  72. dep[x]=dep[f[x]]+;
  73. for(int i=head[x];i;i=e[i].ne)
  74. {
  75. int dd=e[i].to;
  76. if(dd==f[x])continue ;
  77. f[dd]=x;
  78. df1(dd);
  79. siz[x]+=siz[dd];
  80. if(!son[x]||siz[son[x]]<siz[dd])
  81. son[x]=dd;
  82. }
  83. }
  84.  
  85. void dfs(int x,int tp)
  86. {
  87. top[x]=tp;
  88. dfn[x]=++num;
  89. if(son[x])dfs(son[x],tp);
  90. for(int i=head[x];i;i=e[i].ne)
  91. {
  92. int dd=e[i].to;
  93. if(dd==f[x]||dd==son[x])continue;
  94. dfs(dd,dd);
  95. }
  96. }
  97.  
  98. void df2(int x,int v)
  99. {
  100. a[++tot].v=v;
  101. a[tot].pos=dfn[x];
  102. for(int i=head[x];i;i=e[i].ne)
  103. {
  104. int dd=e[i].to;
  105. if(dd==f[x])continue;
  106. df2(dd,e[i].v);
  107. }
  108. }
  109.  
  110. bool check(int x,int y,int v)
  111. {
  112. int l=lx[v],r=rx[v];
  113. int mid;
  114. while(l<=r){
  115. mid=(l+r)>>;
  116. if(a[mid].pos<=y&&a[mid].pos>=x)return ;
  117. else if(a[mid].pos>y)r=mid-;
  118. else l=mid+;
  119. }
  120. return ;
  121. }
  122.  
  123. int main()
  124. {
  125. freopen("pipe.in","r",stdin);
  126. freopen("pipe.out","w",stdout);
  127. scanf("%d",&t);
  128. while(t--)
  129. {
  130. scanf("%d%d",&n,&m);
  131. init();
  132. for(int i=;i<=m;++i)scanf("%d%d%d",&ed[i].fr,&ed[i].to,&ed[i].v);
  133. sort(ed+,ed+m+,cmp);
  134. for(int i=;i<=n;++i)fa[i]=i;
  135. int cnt=;
  136. for(int i=,x,y,fx,fy;i<=m;++i)
  137. {
  138. x=ed[i].fr,y=ed[i].to;
  139. fx=find(x),fy=find(y);
  140. if(fx!=fy){
  141. fa[fx]=fy;
  142. cnt++;
  143. ans+=(ll)ed[i].v;
  144. add(x,y,ed[i].v);
  145. add(y,x,ed[i].v);
  146. pd[i]=;
  147. }
  148. if(cnt==n-)break;
  149. }
  150. printf("%lld\n",ans);
  151. df1();
  152. dfs(,);
  153. df2(,);
  154. sort(a+,a+tot+,cm2);
  155. lx[a[].v]=;
  156. for(int i=;i<=tot;++i)
  157. if(a[i].v!=a[i-].v)lx[a[i].v]=i,rx[a[i-].v]=i-;
  158. rx[a[tot].v]=tot;
  159. for(int i=,x,y,lca,v,deper;i<=m;++i)
  160. if(!pd[i])
  161. {
  162. x=ed[i].fr,y=ed[i].to,v=ed[i].v;
  163. while(top[x]!=top[y]){
  164. if(dep[top[x]]<dep[top[y]])swap(x,y);
  165. if(check(dfn[top[x]],dfn[x],v)){
  166. fla=;
  167. break;
  168. }
  169. x=f[top[x]];
  170. }
  171. if(x!=y){
  172. lca=dep[x]<dep[y]?x:y;
  173. deper=lca==x?y:x;
  174. if(check(dfn[son[lca]],dfn[deper],v))fla=;
  175. }
  176. if(fla)break;
  177. }
  178. if(fla)printf("No\n");
  179. else printf("Yes\n");
  180. }
  181. return ;
  182. }

【次小生成树】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. Django配置session

    在settings.py文件里加入 #配置失效时间为半个小时 SESSION_COOKIE_AGE = 60*30 #关闭浏览器清除cookie SESSION_EXPIRE_AT_BROWSER_C ...

  2. ajax初探--实现简单实时验证

    学习技术最好的方式就是在做中学,做一个小demo来对前端输入进行实时验证. 利用ajax技术和Sevlet技术来实现,使用原生的js. 源码可访问,我的Github 什么是ajax Ajax 即&qu ...

  3. 基于 HTML5 Canvas 的 3D 机房创建

    对于 3D 机房来说,监控已经不是什么难事,不同的人有不同的做法,今天试着用 HT 写了一个基于 HTML5 的机房,发现果然 HT 简单好用.本例是将灯光.雾化以及 eye 的最大最小距离等等功能在 ...

  4. 在非controllers中获取httpServletRequest

    HttpServletRequest request = ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()) ...

  5. 初探Azure的保留实例(Reserved Instance)

    最近的Ignite 2017宣布了Azure将在年底推出保留实例(Reserved Instance).虽然在没有RI的这些年,Azure的EA Monetary Commitment同样也提供了和R ...

  6. SoapUI模拟soap接口返回不同响应(通过groovy脚本)

    一.创建soap项目,输入wsdl文件,然后生成SOAP Mock Service,再生成测试用例,然后新建新的响应 WSDL文件:MathUtil.wsdl <?xml version=&qu ...

  7. xamarin android menu的用法

    在Android中的菜单有如下几种: OptionMenu:选项菜单,android中最常见的菜单,通过Menu键来调用 SubMenu:子菜单,android中点击子菜单将弹出一个显示子菜单项的悬浮 ...

  8. Java SE 8 流库(三)

    1.7. Optional类型 容器对象,可能包含或不包含非空值.如果存在一个值,isPresent()将返回true,get()将返回值.还提供了依赖于包含值是否存在的附加方法,如orElse()( ...

  9. Docker入门书籍

    https://yuedu.baidu.com/ebook/d817967416fc700abb68fca1 精细讲解,入门使用极佳.

  10. date 命令详解

    date - print or set the system date and time Display the current time in the given FORMAT, or set th ...