洛谷T1967 货车运输 Kruskal最大生成树&&倍增LCA

Posted Running-Coder

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了洛谷T1967 货车运输 Kruskal最大生成树&&倍增LCA相关的知识,希望对你有一定的参考价值。

这题的题意是:对于每组x、y,求x到y路径上最小边权的最大值。
于是可以使用最大生成树,因为最大生成树满足性质:生成树中最小边权最大,且任意两点间路径上最小边权最大。
有了树之后,要求路径,那就要考虑LCA。
首先,这题可以树剖,但是我太懒了,于是写了倍增233
具体搞法:
Kruskal跑出最大生成树,然后在树上倍增LCA,处理出2个数组:fa[][]和minv[][];fa[][]表示第2^k个父亲是谁,minv[][]表示当前节点到其第2^k个父亲的路径上的最小边权。对于每次查询,在求LCA的同时,沿路将各段的minv取并即可,每次时间与LCA查询相同,为logn。
现在还有一个问题:图不一定是连通的。。。不能无脑LCA。。。
解决这个问题的方法是:在Kruskal结束后,对并查集中的每个集合各进行一次LCA预处理。对于每次查询,先判断两节点是否属于同一集合,若属于同一集合则进行LCA查询,否则直接输出-1。
代码如下:

  1 #include<cstdio>
  2 #include<iostream>
  3 #include<cstring>
  4 #include<cmath>
  5 #include<ctime>
  6 #include<cstdlib>
  7 
  8 #include<string>
  9 #include<stack>
 10 #include<queue>
 11 #include<vector>
 12 #include<algorithm>
 13 #include<map>
 14 #include<set>
 15 
 16 #define inf 2147483647
 17 #define ri register int
 18 #define ll long long
 19 
 20 using namespace std;
 21 
 22 inline void read(int &x){
 23     x=0;
 24     char t=getchar();
 25     bool f=0;
 26     
 27     while(t<0 || t>9){
 28         if(t==-)f=1;
 29         t=getchar();
 30     }
 31     
 32     while(t>=0 && t<=9){
 33         x=(x<<3)+(x<<1)+t-0;
 34         t=getchar();
 35     }
 36     
 37     if(f)x=-x;
 38 }
 39 
 40 inline void addedge(int,int,int);
 41 int u[100010];
 42 int v[100010];
 43 int w[100010];
 44 int first[10005];
 45 int next[100010];
 46 int pe=0;  //邻接表(无向)
 47 
 48 inline bool cmp(const int a,const int b);
 49 int ha_e[50005];  //边排序辅助数组 
 50 
 51 inline void Kruskal();
 52 int find(int);
 53 int bcj[10005];  //并查集 
 54 bool use[50005];  //记录边是否使用 
 55 
 56 void dfs(int,int);  //LCA预处理,直接父亲及最小边权在上层处理 
 57 bool vis[10005];  //防止重复访问 
 58 int dep[10005];  //节点深度 
 59 int fa[10005][15];  //LCA表 
 60 int minv[10005][15];  //节点到其第2^k个父亲路径间的最小边权 
 61 
 62 inline int query(int,int);  //查询路径最小边权 
 63 
 64 int n,m,q;
 65 int rest;
 66 int x,y,z;
 67 
 68 int main(){
 69     read(n);read(m);
 70     rest=n;
 71     
 72     for(ri i=1;i<=m;i++){
 73         read(x);read(y);read(z);
 74         
 75         addedge(x,y,z);
 76         addedge(y,x,z);
 77         
 78         ha_e[i]=i<<1;
 79     }  //建初始图 
 80     
 81     sort(ha_e+1,ha_e+1+m,cmp);  //边权从大到小排序 
 82     Kruskal();  //生成最大生成树,记录需要使用的边 
 83     
 84     memset(vis,0,sizeof(vis));
 85     
 86     for(ri i=1;i<=n;i++)  //生成2个倍增表 
 87         if(!vis[find(i)]){
 88             fa[i][0]=0;  //根节点无父亲 
 89             minv[i][0]=inf;
 90             dfs(i,1);  //以i号节点为根进行遍历 
 91         }
 92     
 93     read(q);
 94     
 95     for(ri i=1;i<=q;i++){
 96         read(x);read(y);
 97         
 98         if(find(x)!=find(y)){
 99             printf("-1\n");
100             continue;
101         }
102         
103         if(dep[x]>dep[y])swap(x,y);  //使y更深 
104         
105         printf("%d\n",query(x,y));
106     }
107     
108     return 0;
109 }
110 
111 inline void addedge(int x,int y,int z){
112     pe++;
113     u[pe]=x;
114     v[pe]=y;
115     w[pe]=z;
116     next[pe]=first[x];
117     first[x]=pe;
118 }
119 
120 inline bool cmp(const int a,const int b){
121     return w[a]>w[b];
122 }
123 
124 int find(int x){
125     if(bcj[x]==x)return x;
126     else return bcj[x]=find(bcj[x]);
127 }
128 
129 inline void Kruskal(){
130     int x,y,fx,fy;
131     
132     for(ri i=1;i<=n;i++)bcj[i]=i;
133     memset(use,0,sizeof(use));
134     
135     for(ri i=1;i<=m && rest>1;i++){
136         x=u[ha_e[i]];
137         y=v[ha_e[i]];
138         fx=find(x);
139         fy=find(y);
140         
141         if(fx!=fy){
142             bcj[fx]=fy;
143             rest--;
144             use[ha_e[i]>>1]=1;
145         }
146     }
147 }
148 
149 void dfs(int s,int h){
150     int e,t;
151     
152     dep[s]=h;
153     vis[s]=1;
154     
155     for(ri i=1;(1<<i)<h;i++){  //i=0已在上层处理;若1<<i==h则恰有一个节点越界 
156         fa[s][i]=fa[fa[s][i-1]][i-1];
157         minv[s][i]=min(minv[s][i-1],minv[fa[s][i-1]][i-1]);
158     }
159     
160     e=first[s];
161     t=v[e];
162     
163     while(e){
164         if(!vis[t] && use[(e+1)>>1]){
165             fa[t][0]=s;
166             minv[t][0]=w[e];
167             dfs(t,h+1);
168         }
169         
170         e=next[e];
171         t=v[e];
172     }
173 }
174 
175 inline int query(int x,int y){
176     int dt=dep[y]-dep[x];
177     int ans=inf;
178     
179     for(ri i=0;(1<<i)<=dt;i++)
180         if((1<<i)&dt){
181             ans=min(ans,minv[y][i]);
182             y=fa[y][i];
183         }
184     
185     if(x==y)return ans;
186     
187     for(ri i=13;i>=0;i--)
188         if(fa[x][i]!=fa[y][i]){
189             ans=min(ans,minv[x][i]);
190             ans=min(ans,minv[y][i]);
191             
192             x=fa[x][i];
193             y=fa[y][i];
194         }
195     
196     ans=min(ans,minv[x][0]);
197     ans=min(ans,minv[y][0]);
198     
199     return ans;
200 }

以上是关于洛谷T1967 货车运输 Kruskal最大生成树&&倍增LCA的主要内容,如果未能解决你的问题,请参考以下文章

货车运输(洛谷P1967)——生成树+倍增LCA的一通乱搞

luogu1967noip2013 货车运输 [生成树kruskal LCA ]

洛谷P1697货车运输

P1967 货车运输

货车运输

Luogu P1967 货车运输题解