嚴格次小生成樹
阿新 • • 發佈:2020-08-09
洛谷4180
模板
倍增LCA+Kruskal
沒學過這兩個演算法沒關係,後面有講解
時間複雜度O(nlog_2n+mlog_2m)
#include<iostream> #include<cstring> #include<cmath> #include<algorithm> #include<stack> #include<cstdio> #include<queue> #include<map> #include<vector> #include<set> using namespace std; /* 資料中無向圖不保證無自環; 50% 的資料N≤2 000 M≤3 000; 80% 的資料N≤50 000 M≤100 000; 100% 的資料N≤100 000 M≤300 000 ,邊權值非負且不超過 10^9 。 */ const int maxn=1e5+10; const int maxm=3e5+10; const int INF=0x3fffffff; typedef long long LL; typedef unsigned long long ull; struct edge{ int from,to; LL w; }ed[maxm]; struct node{ int nex,to; LL w; }e[maxn<<1]; bool cmp(edge a,edge b){ return a.w<b.w; } int tot; int head[maxn]; void adde(int x,int y,LL w){ e[++tot].nex=head[x]; e[tot].to=y; e[tot].w=w; head[x]=tot; } int n,m,fa[maxn],f[maxn][21],dep[maxn]; //倍增LCA+Kruskal LL mx1[maxn][21],mx2[maxn][21],ans,mst; bool vis[maxm]; int findfa(int x){ if(x==fa[x]) return x; else return fa[x]=findfa(fa[x]); } void kruskal(){ //先求出最小生成樹 sort(ed+1,ed+1+m,cmp); for(int i=1;i<=n;i++) fa[i]=i; for(int i=1,sum=n;i<=m;i++){ if(sum==1) break; int x=findfa(ed[i].from); int y=findfa(ed[i].to); if(x==y) continue; vis[i]=1; mst+=ed[i].w; fa[x]=y; sum--; //在這裡建邊 adde(ed[i].from,ed[i].to,ed[i].w); adde(ed[i].to,ed[i].from,ed[i].w); } } //再倍增的時候更新最小次小 void update(LL &max1,LL &max2,LL x,LL y){ if(max1==x) max2=max(max2,y); else if(max1<x){ max2=max1; max1=x; max2=max(max2,y); } else max2=max(max2,x); } void dfs(int u,int fa){ for(int i=1;i<=20;i++){ f[u][i]=f[f[u][i-1]][i-1]; //倍增 mx1[u][i]=mx1[u][i-1]; mx2[u][i]=mx2[u][i-1]; //mx1[u][i]需要在 mx1[u][i-1] 和 mx1[f[u][i-1]][i-1]之間比較 //mx2也是同理 //注意比較的順序(上面 update(mx1[u][i],mx2[u][i],mx1[f[u][i-1]][i-1],mx2[f[u][i-1]][i-1]); } for(int i=head[u];i;i=e[i].nex){ int v=e[i].to; if(v==fa) continue; dep[v]=dep[u]+1; f[v][0]=u; mx1[v][0]=e[i].w; //最大的初始值 dfs(v,u); //在後面遞迴 } } typedef pair<LL,LL> p; p LCA(int x,int y){ LL max1=0,max2=0; if(dep[x]<dep[y]) swap(x,y); for(int i=20;i>=0;i--){ if(dep[f[x][i]]>=dep[y]){ update(max1,max2,mx1[x][i],mx2[x][i]); x=f[x][i]; } } if(x==y) return make_pair(max1,max2); //返回這條路上的最大和次大值 for(int i=20;i>=0;i--){ if(f[x][i]!=f[y][i]){ update(max1,max2,mx1[x][i],mx2[x][i]); update(max1,max2,mx2[y][i],mx2[y][i]); x=f[x][i]; y=f[y][i]; } } update(max1,max2,mx1[x][0],mx2[x][0]); update(max1,max2,mx1[y][0],mx2[y][0]); return make_pair(max1,max2); } void sol(){ scanf("%d %d",&n,&m); for(int i=1;i<=m;i++){ scanf("%d %d %lld",&ed[i].from,&ed[i].to,&ed[i].w); } kruskal(); dfs(1,0); //求出倍增陣列 } void solve(){ ans=INF; for(int i=1;i<=m;i++){ if(!vis[i]){ p temp=LCA(ed[i].from,ed[i].to); if(ed[i].w==temp.first&&temp.second) ans=min(ans,ed[i].w-temp.second); else if(ed[i].w>temp.first) ans=min(ans,ed[i].w-temp.first); } } printf("%lld",ans+mst); } int main(){ sol(); solve(); return 0; } #include <bits/stdc++.h> #define mp make_pair using namespace std; const int maxn=1e5+10; const int maxe=3e5+10; typedef pair<long long,long long> P; inline int read(){ int x=0,f=1;char ch; do{ch=getchar();if(ch=='-')f=-1;}while(!isdigit(ch)); do{x=x*10+ch-'0';ch=getchar();}while(isdigit(ch)); return f*x; } struct rec{ int from,to; long long w; }edge[maxe]; bool cmp(const rec &a,const rec &b){return a.w<b.w;} struct node{ int nxt,to; long long w; }e[maxn<<1]; int tot=1,head[maxn]; inline void add_edge(int from,int to,long long w){ e[++tot]=node{head[from],to,w},head[from]=tot; } int n,m,fa[maxn],f[maxn][21],dep[maxn]; long long mx1[maxn][21],mx2[maxn][21],ans,mst; bool used[maxe]; int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);} void kruskal(){ sort(edge+1,edge+m+1,cmp); for(int i=1;i<=n;i++)fa[i]=i; for(int i=1,sum=n;i<=m;i++){ if(sum==1)break; int x=find(edge[i].from),y=find(edge[i].to); if(x!=y){ used[i]=1,mst+=edge[i].w,fa[x]=y,--sum; add_edge(edge[i].from,edge[i].to,edge[i].w); add_edge(edge[i].to,edge[i].from,edge[i].w); } } } inline void upd(long long &max1,long long &max2,long long x,long long y){ if(max1==x)max2=max(max2,y); else if(max1<x)max2=max1,max1=x,max2=max(max2,y); else max2=max(max2,x); } void dfs(int u,int fa){ for(int i=1;i<=20;i++){ f[u][i]=f[f[u][i-1]][i-1]; mx1[u][i]=mx1[u][i-1],mx2[u][i]=mx2[u][i-1]; upd(mx1[u][i],mx2[u][i],mx1[f[u][i-1]][i-1],mx2[f[u][i-1]][i-1]); } for(int i=head[u];i;i=e[i].nxt){ int v=e[i].to;if(v==fa)continue; dep[v]=dep[u]+1,f[v][0]=u,mx1[v][0]=e[i].w; dfs(v,u); } } P lca(int x,int y){ long long max1=0,max2=0; if(dep[x]<dep[y])swap(x,y); for(int i=20;i>=0;i--) if(dep[f[x][i]]>=dep[y]){ upd(max1,max2,mx1[x][i],mx2[x][i]); x=f[x][i]; } if(x==y)return mp(max1,max2); for(int i=20;i>=0;i--) if(f[x][i]!=f[y][i]){ upd(max1,max2,mx1[x][i],mx2[x][i]); upd(max1,max2,mx1[y][i],mx2[y][i]); x=f[x][i],y=f[y][i]; } upd(max1,max2,mx1[x][0],mx2[x][0]); upd(max1,max2,mx1[y][0],mx2[y][0]); return mp(max1,max2); } void read_and_parse(){ n=read(),m=read(); for(int i=1;i<=m;i++)edge[i].from=read(),edge[i].to=read(),edge[i].w=read(); kruskal(); dfs(1,0); } void solve(){ ans=0x3f3f3f3f3f3f3f3f; for(int i=1;i<=m;i++)if(!used[i]){ P tmp=lca(edge[i].from,edge[i].to); if(edge[i].w==tmp.first&&tmp.second)ans=min(ans,edge[i].w-tmp.second); else if(edge[i].w>tmp.first)ans=min(ans,edge[i].w-tmp.first); } printf("%lld\n",mst+ans); } int main(){ read_and_parse(); solve(); return 0; }