考察:LCA+Tarjan
思路:
因为N<=1e5,所以上个算法N2 不适用.这里仍然是上道题的思路,枚举所有不在最小生成树的边.求出边两端点u,v.找到u,v在最小生成树的最大值d1和次大值d2.然后通过sum-d1(d2)+road[i].w求解答案.
LCA优化的是求两端点之间的最大值.我们在找两端点u,v LCA的途中,先是将u,v跳至同一层,然后再跳到LCA处.在跳跃途中我们可以找到最大值和次大值.
设d1[u,k] 表示当前结点u与fa[u][k]之间的最大值. d2[u,k] 表示当前结点u与fa[u][k]之间的次大值.
假定k = 1. 那么d1[u,1] = max(d1[u][0],d1[fa[u][0],0) ----> 这样可以递推到k更大的时候 d1[u,k] = max(d1[u][k-1],d1[fa[u][k-1]][k-1])
同理d2[u,k] 但是注意d2[u,k]是在 d1[u][k-1],d1[fa[u][k-1]][k-1],d2[u][k-1],d2[fa[u][k-1]][k-1] 之间求次大值.
然后怎么获得两点的最长距离呢? 我们需要枚举所有跳到LCA途中的最大值和次大值.再在其中挑选最大值和次大值.
1 #include <iostream> 2 #include <cstring> 3 #include <vector> 4 #include <algorithm> 5 #include <queue> 6 using namespace std; 7 const int N = 100010,M = 300010,INF = 0x3f3f3f3f,S = 140; 8 typedef long long LL; 9 struct Road{ 10 int fr,to,ne,w; 11 }road[M<<1]; 12 struct Path{ 13 int u,v,w; 14 bool use; 15 bool operator<(const Path& p){ 16 return this->w<p.w; 17 } 18 }path[M]; 19 int n,m,idx,h[N],p[N],depth[N],fa[N][20],d1[N][20],d2[N][20]; 20 int temp[S]; 21 LL sum; 22 void add(int a,int b,int c) 23 { 24 road[idx].w = c,road[idx].fr = a,road[idx].to = b,road[idx].ne = h[a],h[a] = idx++; 25 } 26 int findf(int x) 27 { 28 if(x!=p[x]) p[x] = findf(p[x]); 29 return p[x]; 30 } 31 void bfs(int s) 32 { 33 queue<int> q; 34 memset(depth,0x3f,sizeof depth); 35 depth[s] = 1; depth[0] = 0; 36 q.push(s); 37 while(q.size()) 38 { 39 int u = q.front(); 40 q.pop(); 41 for(int i=h[u];~i;i=road[i].ne) 42 { 43 int v = road[i].to; 44 if(depth[v]>depth[u]+1) 45 { 46 depth[v] = depth[u]+1; 47 q.push(v); 48 fa[v][0] = u; d1[v][0] = road[i].w,d2[v][0] = -INF;//向上跳一步的最大距离. 49 for(int j=1;j<=18;j++) 50 { 51 fa[v][j] = fa[fa[v][j-1]][j-1]; 52 d1[v][j] = max(d1[v][j-1],d1[fa[v][j-1]][j-1]); 53 int dis_1 = d1[v][j-1],dis_2 = d1[fa[v][j-1]][j-1]; 54 int dis_3 = max(d2[v][j-1],d2[fa[v][j-1]][j-1]); 55 if(dis_1==dis_2) d2[v][j] = dis_3; 56 else if(dis_1>dis_2) d2[v][j] = max(dis_2,dis_3);//次大值 57 else if(dis_2>dis_1) d2[v][j] = max(dis_3,dis_1); 58 } 59 } 60 } 61 } 62 } 63 int lca(int a,int b,int w) 64 { 65 int cnt = 0;//找跳跃途径中的最大值与次大值 66 if(depth[a]<depth[b]) swap(a,b); 67 int max_1 = -INF,max_2 = -INF; 68 for(int i=18;i>=0;i--) 69 if(depth[fa[a][i]]>=depth[b])//同层 70 { 71 temp[++cnt] = d1[a][i]; 72 temp[++cnt] = d2[a][i]; 73 a = fa[a][i]; 74 } 75 if(a!=b) 76 { 77 for(int i=18;i>=0;i--) 78 if(fa[a][i]!=fa[b][i]) 79 { 80 temp[++cnt] = d1[a][i]; temp[++cnt] = d1[b][i]; 81 temp[++cnt] = d2[a][i]; temp[++cnt] = d2[b][i]; 82 a = fa[a][i],b = fa[b][i]; 83 } 84 temp[++cnt] = d1[a][0]; temp[++cnt] = d1[b][0]; 85 temp[++cnt] = d2[b][0]; temp[++cnt] = d2[a][0];//最后还有一层 86 } 87 for(int i=1;i<=cnt;i++) 88 if(temp[i]>max_1) max_2 = max_1,max_1 = temp[i]; 89 else if(temp[i]>max_2&&temp[i]!=max_1) max_2 = temp[i]; 90 if(w==max_1) return max_2; 91 return max_1; 92 } 93 int main() 94 { 95 scanf("%d%d",&n,&m); 96 for(int i=1;i<=n;i++) p[i] = i,h[i] = -1; 97 for(int i=1;i<=m;i++) 98 { 99 int a,b,w; scanf("%d%d%d",&a,&b,&w); 100 path[i] = {a,b,w}; 101 } 102 sort(path+1,path+m+1); 103 for(int i=1;i<=m;i++) 104 { 105 int pa =findf(path[i].u),pb = findf(path[i].v); 106 if(pa==pb) continue; 107 p[pa] = pb; 108 path[i].use = 1; 109 sum+=(LL)path[i].w; 110 add(path[i].u,path[i].v,path[i].w); add(path[i].v,path[i].u,path[i].w); 111 } 112 bfs(1); 113 LL ans = 1e18; 114 for(int i=1;i<=m;i++) 115 if(!path[i].use) 116 { 117 int a = path[i].u,b =path[i].v; 118 ans = min(sum-lca(a,b,path[i].w)+path[i].w,ans); 119 } 120 printf("%lld\n",ans); 121 return 0; 122 }