洛谷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 }