传送门
文章目录
- 题目描述
- 题目解析
- 代码
题目描述
题目解析
本题如果告诉你,城市形成了一棵树,是不是就迎刃而解了呢?
本题的关键就是把稠密图转化为一片森林(就是可能有很多棵树啦)
怎么转化呢?
考虑哪些边是没用的
如果AB已经可以联通且限重很大,再给我一条A到B限重很小的边肯定没有用了
换句话说,我们有用的边就是本图中的一座最大生成森林
(不明白这个词的童鞋可以类比一下最小生成树的概念)
怎么证明呢?
很容易
考虑怎么生成最大森林的:
每次选出未选的权值最大的边,如果两边的点未联通就连上
假设A到B的最好路径有一条边X不在这个森林上
那么这条边的权值肯定大于森林上AB路径的最小值Y(这样森林上的路径才不是最优的)
那么我们建造森林时肯定会使X在Y之前枚举到
而此时AB不连通(因为枚举到Y时还不连通)
那么应该把X加入森林中
和“X不在这个森林上"矛盾
证完啦(自己写的证明,可能有些草率 )
转化为树上问题后就非常简单
倍增,dfs序,树剖,都可以啦(还有很多做法但我还不会。。。)
联通判断常规并查集就OK了
说起来还是倍增好写
那就写倍增啦
倍增第一次少取了一个min还挂掉了。。。
代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define mid ((l+r)>>1)
#define ls k<<1
#define rs k<<1|1
const int N=2e5+100;
const int M=2e4+100;int n,m,q;
int a,b,c;struct node{int to,nxt,v;
}p[N];
int fi[N],cnt=-1;
void addline(int x,int y,int v){
// printf("addline:x=%d y=%d v=%d\n",x,y,v);p[++cnt]=(node){y,fi[x],v};fi[x]=cnt;
}int fa[N];
int find(int x){return fa[x]==x ? x : fa[x]=find(fa[x]);}
void merge(int a,int b){int aa=find(a),bb=find(b);fa[aa]=bb;
}int dep[N],f[N],up[N];
void dfs(int x,int ffa){f[x]=ffa;dep[x]=dep[ffa]+1;for(int i=fi[x];~i;i=p[i].nxt){int to=p[i].to;if(to==ffa) continue;up[to]=p[i].v;dfs(to,x);}return;
} struct node2{int x,y,v;bool operator < (const node2 o)const {return v>o.v;}
}e[N];struct node3{int pl,mn;
}dp[M][20];
int mi[20];
void solve(){mi[0]=1;for(int i=1;i<=16;i++) mi[i]=mi[i-1]<<1;for(int i=1;i<=n;i++){dp[i][0].pl=f[i];dp[i][0].mn=up[i];
// printf("ok i=%d k=0 pl=%d mn=%d\n",i,dp[i][0].pl,dp[i][0].mn);}for(int k=1;k<=15;k++){for(int i=1;i<=n;i++){if(dep[i]>=mi[k]){dp[i][k].pl=dp[dp[i][k-1].pl][k-1].pl;dp[i][k].mn=min(dp[i][k-1].mn,dp[dp[i][k-1].pl][k-1].mn);
// printf("i=%d k=%d pl=%d mn=%d\n",i,k,dp[i][k].pl,dp[i][k].mn);}}}
}int lca(int x,int y){int ans=2e9;if(dep[y]>dep[x]) swap(x,y);for(int k=15;k>=0;k--){if(dep[x]-mi[k]<dep[y]) continue;ans=min(ans,dp[x][k].mn);x=dp[x][k].pl;
// printf("ok x=%d y=%d ans=%d\n",x,y,ans);}for(int k=15;k>=0;k--){if(mi[k]>dep[x]) continue;if(dp[x][k].pl==dp[y][k].pl) continue;ans=min(ans,dp[x][k].mn);x=dp[x][k].pl;ans=min(ans,dp[y][k].mn);y=dp[y][k].pl;
// printf("x=%d y=%d ans=%d\n",x,y,ans);}if(x!=y) ans=min(ans,min(dp[y][0].mn,dp[x][0].mn));//这里x和y的dp都要考虑取min,显然不一样啊!!!return ans;
}int main(){memset(fi,-1,sizeof(fi));memset(dep,-1,sizeof(dep));scanf("%d%d",&n,&m);for(int i=1;i<=n;i++) fa[i]=i;for(int i=1;i<=m;i++) scanf("%d%d%d",&e[i].x,&e[i].y,&e[i].v);sort(e+1,e+1+m);for(int i=1;i<=m;i++){int xx=find(e[i].x),yy=find(e[i].y);if(xx==yy) continue;merge(xx,yy);addline(e[i].x,e[i].y,e[i].v);addline(e[i].y,e[i].x,e[i].v);}for(int i=1;i<=n;i++){if(dep[i]==-1){dep[i]=0;dfs(i,0);}}solve();scanf("%d",&q);for(int i=1;i<=q;i++){scanf("%d%d",&a,&b);if(find(a)!=find(b)) printf("-1\n");else printf("%d\n",lca(a,b));}return 0;
}
/*
11 9
1 2 8
1 3 15
1 7 7
2 4 12
3 6 9
3 5 6
8 9 11
8 10 10
10 11 15
100
*/