dr就这样咕咕咕了好几天
(一时咕,一时爽,一直咕,那就gg了)
我才不会承认
我看题(概念)看了好久都没懂
-----------------------------------------------------
一、定义
给出一个图
可以形成若干个树
将形成的树 按总比边权和的大小 从小到大排序
第二小的就是次小生成树
这时需要注意啦:
若只是按排完序后的第二个树
得到的是 不严格次小生成树
因为 有可能排序后的第二个树 和 第一个树相等
那么
严格次小生成树 就是 和 第一棵树不等的 剩余树中的 最小的树
(终于整明白定义了,就开始想想怎么做吧)
二、算法
先求出最小生成树
(个人喜欢用kruskal)
然后枚举出非树边
把非树边加入到树中
就会形成一个环
就要删边
(看看 哪次 加入非树边再删边 得到的生成树 是最小的<当然要大于最小生成树啦>)
(每枚举到一条非树边 就是 一种情况的(一棵树)的判断)
删边的原则?
如果加进来的边与环上最大值不同,直接删掉最大值
如果加进来的边与环上最大值相同,就删掉次大值
如何高效的去找要删的边咧?(维护最大值和次大值)
倍增LCA!!
fa维护LCA
maxi维护最大值
mini维护次大值
#include<cstdio> #include<algorithm> #include<iostream> #define ll long long using namespace std; const ll N = 400010; const ll M = 900010; const ll INF = 2147483647000000; inline ll read()//快读 { ll sum = 0,p = 1; char ch = getchar(); while(ch < '0' || ch > '9') { if(ch == '-') p = -1; ch = getchar(); } while(ch >= '0' && ch <= '9') { (sum *= 10) += ch - '0'; ch = getchar(); } return sum * p; } struct edge { ll u,v,d; ll next; }G[N << 1]; ll tot,head[N]; edge A[M<<1]; bool vis[M<<1]; ll fa[N]; ll f[N][19]; ll maxi[N][19]; ll mini[N][19]; ll deep[N]; ll n,m; inline bool cmp(edge x,edge y)//快排 { return x.d < y.d; } inline ll getfa(ll o)//找根节点 { if(o == fa[o]) return o; else return fa[o] = getfa(fa[o]); } inline void add(ll u,ll v,ll d)//加边 { G[++tot].u = u; G[tot].v = v; G[tot].d = d; G[tot].next = head[u]; head[u] = tot; } inline void dfs(ll o,ll x)//预处理 { f[o][0] = x; for(ll i = head[o];i;i = G[i].next) { ll v = G[i].v; if(v == x) continue; deep[v] = deep[o] + 1LL; maxi[v][0] = G[i].d; mini[v][0] = -INF; dfs(v,o); } } inline void cal()//倍增的思想求出最大值和次大值 { for(ll i = 1;i <= 18;i++) for(ll j = 1;j <= n;j++) { f[j][i] = f[f[j][i - 1]][i - 1]; maxi[j][i] = max(maxi[j][i - 1],maxi[f[j][i - 1]][i - 1]); mini[j][i] = max(mini[j][i - 1],mini[f[j][i - 1]][i - 1]); if(maxi[j][i - 1] > maxi[f[j][i - 1]][i - 1]) mini[j][i] = max(mini[j][i],maxi[f[j][i - 1]][i - 1]); else if(maxi[j][i - 1] < maxi[f[j][i - 1]][i - 1]) mini[j][i] = max(mini[j][i],maxi[j][i - 1]); } } inline ll lca(ll x,ll y)//倍增lca { if(deep[x] < deep[y]) swap(x,y); for(ll i = 18;i >= 0;i--) if(deep[f[x][i]] >= deep[y]) x = f[x][i]; if(x == y) return x; for(ll i = 18;i >= 0;i--) if(f[x][i] ^ f[y][i]) { x = f[x][i]; y = f[y][i]; } return f[x][0]; } inline ll qmax(ll u,ll v,ll maxx)//找最大值(或次大值)-- 倍增的思想 { ll ans = -INF; for(ll i = 18;i >= 0;i--) { if(deep[f[u][i]] >= deep[v]) { if(maxx != maxi[u][i]) ans = max(ans,maxi[u][i]); else ans = max(ans,mini[u][i]); u = f[u][i]; } } return ans; } int main() { n = read(),m = read(); for(ll i = 1;i <= m;i++) A[i].u = read(),A[i].v = read(),A[i].d = read(); sort(A+1,A+m+1,cmp); for(ll i = 1;i <= n;i++) fa[i] = i; ll cnt = 0LL; for(ll i = 1;i <= m;i++)//kruscal { ll u = getfa(A[i].u); ll v = getfa(A[i].v); if(u != v) { cnt += A[i].d; fa[u] = v; add(A[i].u,A[i].v,A[i].d); add(A[i].v,A[i].u,A[i].d); vis[i] = true; } } mini[1][0] = -INF; deep[1] = 1; dfs(1,-1); cal();//维护最大值和次大值 ll ans = INF; for(ll i = 1;i <= m;i++) { if(!vis[i])//没有被加过边(不在生成图中) { ll u = A[i].u; ll v = A[i].v; ll d = A[i].d; ll o = lca(u,v); ll maxu = qmax(u,o,d); ll maxv = qmax(v,o,d); ans = min(ans,cnt - max(maxu,maxv) + d); } } printf("%lld",ans); return 0; }