AcWing 356. 次小生成树
作者:互联网
题目:给定一张$N$个点$M$条边的无向图,求无向图的严格次小生成树。
严格次小生成树:设最小生成树的边权之和为$\mathrm{sum}$,严格次小生成树就是指边权之和大于$\mathrm{sum}$的生成树中最小的一个。
输入格式: 第一行包含两个整数$N$和$M$。 接下来$M$行,每行包含三个整数$x,y,z$,表示点$x$和点$y$之前存在一条边,边的权值为$z$。
输出格式 :包含一行,仅一个数,表示严格次小生成树的边权和。(数据保证必定存在严格次小生成树)
数据范围 :$N\leqslant 10^5,M\leqslant 3\times10^5$。
分析:我们先求出原图的最小生成树,然后枚举每一条非树边,看看当前两点构成的路径上最大边与次大边与该非树边的大小关系,由于求出的最小生成树是选出的最短边,因此非树边一定满足大于等于这两点路径上的所有边,当然也满足大于等于最大边。若等于最大边,则我们将次大边替换成非树边,否则替换最大边为非树边。树上两点路径上的最大边与次大边可以在求$\mathrm{LCA}$时也求出来,具体地,我们设$g[x][y][0]$表示点$x$向上走$2^y$步中的次大边,$g[x][y][1]$表示点$x$向上走$2^y$步中的最大边。那么我们可以先求出走到$2^{y-1}$点的最大边,然后再走$2^{y-1}$步,对这两步中的最大边取最大值即可;对于次大边,如果这两个模块内的最大边相等,则我们用这两个模块的次大边更新;如果第一阶段的最大边$>$第二阶段的最大边,那么最大边在第一阶段的次大边与第二阶段的最大边中取最大值,不然则反之。
#include <iostream> #include <algorithm> #include <cstring> #include <queue> #include <climits> using namespace std; using LL = long long; const int N = 1e5+10, M = 3e5+10; struct Node{ int x, y, z; bool operator<(const Node& t)const{ return z<t.z; } }edge[M]; int n, m, p[N]; int h[N], e[N*2], ne[N*2], w[N*2], idx; int f[N][18], g[N][18][2], d[N]; bool st[M]; void add(int a, int b, int c){ e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++; } int find(int x){ return x==p[x]?x:p[x] = find(p[x]); } void merge(int c[2], int a[2], int b[2]){ //a[0], b[0]分别为两段的最大值,a[1],b[1]分别为两段的次大值 //a[0]>a[1], b[0]>b[1] if(a[0] == b[0])c[1] = max(a[1], b[1]); else if(a[0]>b[0])c[1] = max(a[1], b[0]); else c[1] = max(a[0], b[1]); c[0] = max(a[0], b[0]); } void bfs(int u){ d[u] = 1; queue<int> q; q.push(u); while(q.size()){ auto t = q.front(); q.pop(); for(int i=h[t]; ~i; i=ne[i]){ int j = e[i]; if(!d[j]){ d[j] = d[t] + 1; q.push(j); f[j][0] = t; g[j][0][0] = w[i]; for(int k=1; k<=17; k++){ f[j][k] = f[f[j][k-1]][k-1]; merge(g[j][k], g[j][k-1], g[f[j][k-1]][k-1]); } } } } } int lca(int res[], int x, int y){ res[0] = res[1] = 0; if(d[x]<d[y])swap(x, y); for(int i=17; i>=0; i--){ if(d[f[x][i]]>=d[y]){ merge(res, res, g[x][i]); x = f[x][i]; } } if(x==y) return x; for(int i=17; i>=0; i--){ if(f[x][i] != f[y][i]){ merge(res, res, g[x][i]); merge(res, res, g[y][i]); x = f[x][i], y = f[y][i]; } } merge(res, res, g[x][0]); merge(res, res, g[y][0]); return f[x][0]; } int main(void){ cin>>n>>m; memset(h, -1, sizeof h); for(int i=0; i<m; i++)scanf("%d %d %d", &edge[i].x, &edge[i].y, &edge[i].z); sort(edge, edge+m); for(int i=0; i<m; i++)p[i] = i; LL res = 0; for(int i=0; i<m; i++){ int x = find(edge[i].x), y = find(edge[i].y), z = edge[i].z; if(x != y){ p[x] = y; res += z; st[i] = true; add(edge[i].x, edge[i].y, z); add(edge[i].y, edge[i].z, z); } } bfs(1); int delta = INT_MAX; for(int i=0; i<m; i++){ if(!st[i]){//选择非树边 int x = edge[i].x, y = edge[i].y, z = edge[i].z, res[2]; lca(res, x, y); if(z == res[0])delta = min(delta, z-res[1]);//树边的最长边等于当前的非树边 else if(res[1])delta = min(delta, z-res[0]); } } cout<<res + delta<<endl; return 0; }
标签:merge,int,res,大边,生成,356,include,AcWing 来源: https://www.cnblogs.com/Hibert/p/15613131.html