• 次小生成树(lca+Kruskal)


    定理:

    对于一张无向图,如果存在最小生成树和(严格)次小生成树,那么对于任何一颗最小生成树看,都存在一颗(严格次小生成树,使得这两棵树只有一条边不同

    最小生成树大家应该都不陌生, 次小生成树就是边权和大于等于最小生成树的另一颗树,也就是边权之和第二小的生成树, 有严格次小生成树非严格次小生成树
    边权之和严格大于最小生成树的且权值最小的树,就是严格次小生成树
    若求得的另一颗树与最小生成树权值相等, 则为非严格的次小生成树

     练习

    给定一张 N 个点 M条边的无向图,求无向图的严格次小生成树。

    设最小生成树的边权之和为 sum,严格次小生成树就是指边权之和大于 sum的生成树中最小的一个。

    输入格式

    第一行包含两个整数 N和 M。

    接下来 M行,每行包含三个整数 x,y,z,表示点 x 和点 y 之前存在一条边,边的权值为 z

    输出格式

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

    数据范围

    N≤105,M≤3×105

    输入样例:

    1. 5 6
    2. 1 2 1
    3. 1 3 2
    4. 2 4 3
    5. 3 5 4
    6. 3 4 3
    7. 4 5 6

    输出样例:

    11
    

    思路

    先求出最小生成树,再枚举每条非树边

    AC_code

    1. #include
    2. #include
    3. #include
    4. #include
    5. using namespace std;
    6. typedef long long LL;
    7. const int N=1e5+10,M=3e5+10,INF=0x3f3f3f3f;
    8. struct node
    9. {
    10. int a,b,w;
    11. bool used;
    12. }edge[M];
    13. int n,m;
    14. int h[N],w[M],e[M],ne[M],idx;//建图
    15. int depth[N],fa[N][18];//求lca时预处理的数据,具体见倍增法求lca
    16. int dist1[N][18],dist2[N][18];
    17. //dist1:结点i向上跳2^j步所经过的最大边权
    18. //dist2:结点i向上跳2^j步所经过的次大边权
    19. int p[N];
    20. int q[M];
    21. int find(int x)//并查集
    22. {
    23. if(p[x]!=x) p[x]=find(p[x]);
    24. return p[x];
    25. }
    26. void add(int a,int b,int c)//加边
    27. {
    28. e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
    29. }
    30. bool cmp(node a,node b)
    31. {
    32. return a.w
    33. }
    34. /*
    35. kruskal算法求最小生成树
    36. 01:将所有边按从小到达排序
    37. 02:枚举每条边,若结点a和结点b不连通(并查集),将a和b连接
    38. */
    39. LL Kruskal()
    40. {
    41. for(int i=1;i<=m;i++)
    42. {
    43. int a,b,c;
    44. scanf("%d%d%d",&a,&b,&c);
    45. edge[i]={a,b,c};
    46. }
    47. for(int i=1;i<=n;i++) p[i]=i;//并查集初始化
    48. sort(edge+1,edge+1+m,cmp);
    49. LL res=0;//最小生成树的边权和
    50. for(int i=1;i<=m;i++)
    51. {
    52. //if(!edge[i].used)
    53. //{
    54. int a=find(edge[i].a),b=find(edge[i].b),w=edge[i].w;
    55. if(a!=b)
    56. {
    57. p[a]=b;
    58. res+=w;
    59. edge[i].used=true;
    60. }
    61. //}
    62. }
    63. return res;
    64. }
    65. void build()
    66. {
    67. memset(h,-1,sizeof h);
    68. for(int i=1;i<=m;i++)
    69. {
    70. int a=edge[i].a,b=edge[i].b,w=edge[i].w;
    71. if(edge[i].used)
    72. {
    73. add(a,b,w);
    74. add(b,a,w);
    75. }
    76. }
    77. }
    78. void bfs()
    79. {
    80. memset(depth,0x3f,sizeof depth);
    81. depth[0]=0;
    82. depth[1]=1;
    83. queue<int> q;
    84. q.push(1);
    85. while(!q.empty())
    86. {
    87. int t=q.front();
    88. q.pop();
    89. for(int i=h[t];~i;i=ne[i])
    90. {
    91. int j=e[i];
    92. if(depth[j]>depth[t]+1)
    93. {
    94. depth[j]=depth[t]+1;
    95. q.push(j);
    96. fa[j][0]=t;
    97. dist1[j][0]=w[i],dist2[j][0]=-INF;
    98. for(int k=1;k<18;k++)
    99. {
    100. int anc=fa[j][k-1];
    101. fa[j][k]=fa[anc][k-1];
    102. int distance[4]={dist1[j][k-1],dist2[j][k-1],dist1[anc][k-1],dist2[anc][k-1]};
    103. dist1[j][k]=dist2[j][k]=-INF;
    104. for(int u=0;u<4;u++)
    105. {
    106. int d=distance[u];
    107. if(d>dist1[j][k])
    108. {
    109. dist2[j][k]=dist1[j][k];
    110. dist1[j][k]=d;
    111. }
    112. else if(d!=dist1[j][k]&&d>dist2[j][k])
    113. {
    114. dist2[j][k]=d;
    115. }
    116. }
    117. }
    118. }
    119. }
    120. }
    121. }
    122. int lca(int a,int b,int w)
    123. {
    124. static LL distance[2*N];
    125. //存下结点a与结点b向上跳的过程中的的最大边和次大边
    126. int cnt=0;
    127. //深度大的先跳
    128. if(depth[a]swap(a,b);
    129. //lca
    130. //01:先跳到同一层
    131. for(int k=17;k>=0;k--)
    132. {
    133. if(depth[fa[a][k]]>=depth[b])
    134. {
    135. distance[++cnt]=dist1[a][k];
    136. distance[++cnt]=dist2[a][k];
    137. a=fa[a][k];
    138. }
    139. }
    140. //02:如果a!=b,a与b同时向上跳,直到跳到其最近公共祖先的下一层
    141. if(a!=b)
    142. {
    143. for(int k=17;k>=0;k--)
    144. {
    145. if(fa[a][k]!=fa[b][k])
    146. {
    147. distance[++cnt]=dist1[a][k];
    148. distance[++cnt]=dist2[a][k];
    149. distance[++cnt]=dist1[b][k];
    150. distance[++cnt]=dist2[b][k];
    151. a=fa[a][k],b=fa[b][k];
    152. }
    153. }
    154. // 此时a和b到lca下同一层 所以还要各跳1步=跳2^0步
    155. distance[++cnt]=dist1[a][0];
    156. distance[++cnt]=dist1[b][0];
    157. }
    158. int d1,d2;
    159. d1=d2=-INF;
    160. for(int i=1;i<=cnt;i++)
    161. {
    162. int d=distance[i];
    163. if(d>d1)
    164. {
    165. d2=d1;
    166. d1=d;
    167. }
    168. else if(d!=d1&&d>d2)
    169. {
    170. d2=d;
    171. }
    172. }
    173. if(w>d1) return w-d1;
    174. if(w>d2) return w-d2;
    175. return INF;
    176. }
    177. int main()
    178. {
    179. cin>>n>>m;
    180. LL sum=Kruskal();//最小生成树的边权和
    181. build();//将最小生成树建出来
    182. bfs();//倍增法求lca的预处理
    183. LL res=1e18;
    184. for(int i=1;i<=m;i++)
    185. {
    186. //枚举非树边
    187. if(!edge[i].used)
    188. {
    189. int a=edge[i].a,b=edge[i].b,w=edge[i].w;
    190. res=min(res,sum+lca(a,b,w));
    191. //lca返回w-(a与b之间最大边或者次大边的长度)
    192. }
    193. }
    194. cout<
    195. return 0;
    196. }

  • 相关阅读:
    FPGA project : DS18B20
    《深入了解java虚拟机》高效并发读书笔记——Java内存模型,线程,线程安全 与锁优化
    SLCP(Social and Labor Convergence Project) 社会劳工整合项目
    深入理解 pytest Fixture 方法及其应用
    数组:
    人大与加拿大女王大学金融硕士项目——立即行动,才是缓解焦虑的解药
    关于指针与引用传递的效率问题
    2、灰度图
    可视化学习:如何用WebGL绘制3D物体
    Java-day15(Java常用类)
  • 原文地址:https://blog.csdn.net/weixin_62224014/article/details/127599212