原题链接:https://www.luogu.org/problem/show?pid=1967
说出来你可能不信,这题我爆了一整天的0。想获取更多真相者,请打开洛谷p1967题评测界面,输入用户名“zx2000412”,你就知道现状有多惨烈。
先说说思路吧。听说这道题是最大生成树,我就按照这个思路往下展开的。
一开始的想法是跑一遍最大生成树,以获得的树的边再建一图,对于每次询问跑一次bfs。
后来发现时间复杂度是平方阶,肯定不是标算,只拿了5分(我不得不说,这题数据真心强
考虑到每次是在树上询问任意两点最大载重量,遂想到LCA做法。最后跑了380ms,差强人意。
听说树剖做法更快?改天试一下。
依然是要处理出最大生成树,存起来。用两个倍增数组,一个是f[i][j],代表从i这个点向上蹦2^j个点是哪个点,另一个是minval[i][j]代表从i向上蹦2^j个点时经过路径的最小值。
(求最大载重量为什么算最小?道理很显然啊,不可以超重,所以最大载重量一定是最小道路承载量)
求生成树是裸的Kruskal,并查集做法,这里不再赘述。细节部分详见代码。(这年头谁还用Prim)
然后是二次建图,这里有一步dfs预处理,把这个图转换成树一样的形式(虽然本来就是树)dep数组记录深度方便lca使用。
求LCA时也需要先预处理倍增数组,然后蹦的时候顺带维护一下ans就好。
参考代码:
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 #define maxn 50005 6 #define INF 888888888888 7 using namespace std; 8 typedef long long ll; 9 //------------------------变量与结构体--------------------------- 10 ll dep[maxn],f[maxn][21],minval[maxn][21]; 11 ll n,m,q,x,y; 12 ll tot = 0,head[maxn],father[maxn],fa[maxn]; 13 struct Edge{ 14 ll from,to,dis; 15 bool operator<(const Edge& rhs)const{ 16 return dis > rhs.dis; 17 } 18 }; 19 Edge edge_T[maxn]; 20 Edge edge[maxn]; 21 //-------------------------辅助类函数----------------------------- 22 ll read(){ 23 ll num = 0; 24 char c; 25 bool flag = false; 26 while ((c = getchar()) == ' ' || c == ' ' || c == ' '); 27 if (c == '-') 28 flag = true; 29 else 30 num = c - '0'; 31 while (isdigit(c = getchar())) 32 num = num * 10 + c - '0'; 33 return (flag ? -1 : 1) * num; 34 } 35 ll min_ele(ll a,ll b,ll c){ 36 ll t = min(a,b); 37 return min(t,c); 38 } 39 //---------------------Kruskal与二次建图-------------------------- 40 41 void init_ufs(){ 42 for (register ll i=1;i<=n;i++) 43 father[i] = i; 44 } 45 46 ll find(ll x){ 47 if (father[x] == x) 48 return x; 49 father[x] = find(father[x]); 50 return father[x]; 51 } 52 void merge(ll x, ll y){ 53 x = find(x); 54 y = find(y); 55 father[y] = x; 56 } 57 58 void add_edge(ll from,ll to,ll dis){ 59 edge[++tot].from = head[from]; 60 edge[tot].to = to; 61 edge[tot].dis = dis; 62 head[from] = tot; 63 } 64 void dfs(ll x){ 65 for (register ll i = head[x];i;i = edge[i].from){ 66 ll v = edge[i].to; 67 if (v == fa[x]) 68 continue; 69 f[v][0] = x; 70 minval[v][0] = edge[i].dis; 71 dep[v] = dep[x] + 1; 72 fa[v] = x; 73 dfs(v); 74 } 75 } 76 //--------------------------LCA相关------------------------------ 77 void init_lca(){ 78 for (register ll i=1;i<=20;i++) 79 for (register ll j=1;j<=n;j++){ 80 f[j][i] = f[ f[j][i-1] ][i-1]; 81 minval[j][i] = min(minval[j][i-1],minval[ f[j][i-1] ][i-1]); 82 } 83 } 84 ll lca(ll x,ll y){ 85 ll ans = INF; 86 if (dep[x]>dep[y]) 87 swap(x,y); 88 for (register ll i=20;i>=0;i--) 89 if (dep[f[y][i]]>=dep[x]){ 90 ans=min(ans,minval[y][i]); 91 y=f[y][i]; 92 } 93 if (x==y) 94 return ans; 95 for (register ll i=20;i>=0;i--){ 96 if (f[x][i] != f[y][i]){ 97 ans = min_ele(minval[x][i],minval[y][i],ans); 98 x = f[x][i]; 99 y = f[y][i]; 100 } 101 } 102 ans = min_ele(minval[x][0],minval[y][0],ans); 103 return ans; 104 } 105 //----------------------------主函数---------------------------- 106 int main(){ 107 n = read();m = read(); 108 for (register ll i=1;i<=m;i++){ 109 edge_T[i].from = read(); 110 edge_T[i].to = read(); 111 edge_T[i].dis = read(); 112 } 113 sort(edge_T+1,edge_T+m+1); 114 init_ufs(); 115 for (register ll i=1;i<=m;i++) 116 if (find(edge_T[i].from) != find(edge_T[i].to)){ 117 merge(edge_T[i].from,edge_T[i].to); 118 add_edge(edge_T[i].from, edge_T[i].to, edge_T[i].dis); 119 add_edge(edge_T[i].to, edge_T[i].from, edge_T[i].dis); 120 } 121 for (register ll i=1;i<=n;i++) 122 if (father[i] == i){ 123 fa[i] = i;dep[i] = 1;dfs(i); 124 } 125 init_lca(); 126 q = read(); 127 for (register ll i=1;i<=q;i++){ 128 x = read();y = read(); 129 if (find(x) == find(y)) 130 printf("%lld ",lca(x,y)); 131 else 132 printf("-1 "); 133 } 134 return 0; 135 }