AcWing 356. Secondary Small Spanning Tree

Keywords: AcWing

Title: Given an undirected graph of $N$points$M$bars, find the strictly minor spanning tree of the undirected graph.

Strict Minor Spanning Tree: Set the sum of the edge weights of the minimum spanning tree to $\mathrm{sum}$. Strict Minor Spanning Tree is the smallest spanning tree whose sum of edge weights is greater than $\mathrm{sum}$.

Input format: The first line contains two integers, $N$and $M$. Next, $M$lines, each containing three integers, $x,y,z$, indicate that an edge exists before the point, $x$and point, $y$with a weight of $z$.

Output format: Contains one row, only one number, representing the edge weight and strictly minor spanning trees. (Strict sub-spanning trees are guaranteed to exist for data)

Data range: $N\leqslant 10^5,M\leqslant 3\times10^5$.

Analysis: We first find the minimum spanning tree of the original map, and then enumerate each non-tree edge to see the relationship between the maximum edge and the second largest edge on the current two-point path and the size of the non-tree edge. Since the minimum spanning tree is the shortest selected edge, the non-tree edge must satisfy all the edges on the path greater than or equal to these two points, and of course, the non-tree edge must also satisfy the greater or equal maximum edge. If it is equal to the maximum edge, we will replace the second largest edge with a non-tree edge, otherwise replace the largest edge with a non-tree edge. The largest and second largest edges on a tree's two-point path can also be found when asking for $\mathrm{LCA}$Specifically, we set $g[x][y][0]$to represent the second largest edge in a step of $2^y$and $g[x][y][1]$to represent the largest edge in a step of $2^y$ Then we can find the maximum edge to reach $2^{y-1}$point and then go $2^{y-1}$step to get the maximum edge in both steps. For the minor major edge, if the largest edges within the two modules are equal, we update them with the minor major edges of the two modules. If the maximum edge in the first stage $>$the maximum edge in the second stage, then the maximum edge is taken from the first and second largest edges in the first stage, otherwise the opposite is true.

#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;
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]Maximum value for two segments,a[1],b[1]Secondary maximum of two segments
    //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;
        auto t = q.front();
        for(int i=h[t]; ~i; i=ne[i]){
            int j = e[i];
                d[j] = d[t] + 1;
                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--){
            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){
    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);
    int delta = INT_MAX;
    for(int i=0; i<m; i++){
        if(!st[i]){//Select non-tree edges
            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]);//The longest side of a tree edge equals the current non-tree edge
            else if(res[1])delta = min(delta, z-res[0]);
    cout<<res + delta<<endl;
    return 0;


Posted by EY on Tue, 30 Nov 2021 14:07:41 -0800