具体思路:
最大生成树 + 倍增LCA
首先感谢tqc大佬为我的耐心修改代码(你可能是改了个shi… …)
贴代码
include
include
include
include
include
using namespace std;
const int maxn = 100005;
const int maxm = 100005;
int n,m,head[maxn],father[maxn],top,depth[maxn],par[maxn][22],pre[maxn][22];
int ans = 1e9;
struct edge_pro {
int st,en,value;
}ea[maxm<<1];
//题目要求的树
struct edge {
int next,to,value;
}e[maxm<<1];
//最大生成树里的树
bool cmp (edge_pro a,edge_pro b) {
return a.value >b.value;
}
int find(int x) {
if(father[x] == x) return x;
return father[x] = find(father[x]);
}
void unionn (int r1,int r2) {
if(find(r1) != find(r2))
father[find(r1)] = find(r2);
}
void add_edge(int u,int v,int w) {
e[++top].next = head[u];
e[top].value = w;
e[top].to = v;
head[u] = top;
}
void dfs(int u) { //这个dfs和我的dfs不是很一样??
for(int i = head[u] ; i ;i = e[i].next) {
if(depth[e[i].to]) continue;
depth[e[i].to] = depth[u] + 1;
pre[e[i].to][0] = e[i].value;
par[e[i].to][0] = u;
dfs(e[i].to);
}
}
void init() {
for(int i = 1;i <= log(n); ++i)
for(int j = 1;j <= n ;++j) {
par[j][i] = par[par[j][i - 1]][i - 1];
pre[j][i] = min(pre[j][i - 1],pre[par[j][i - 1]][i - 1]);
}
}
int lca(int x,int y) {
if(find(x) != find(y)) return -1;
ans = 1e9;
if(depth[x] < depth[y]) swap(x , y);
for(int i = log(n);i >= 0 ;–i)
if(depth[par[x][i]] >= depth[y]) {
ans = min(pre[x][i] , ans); //重点!重点!(本人在这里卡了一万次)
x = par[x][i]; //这两行顺序不能乱的哦 需要先更新ans 再去重新赋值x 下面也是
}
if(x == y) return ans;
for(int j = log(n);j >= 0 ;–j)
if(par[x][j] != par[y][j]){
ans = min(ans , min(pre[x][j] , pre[y][j]));
x = par[x][j] , y = par[y][j];
}
ans = min(ans , min(pre[x][0] , pre[y][0]));
return ans;
}
int main() {
scanf(“%d%d”,&n,&m);
for(int i = 1;i <= m ;++i) {
int x,y,z; scanf(“%d%d%d”,&x,&y,&z);
ea[i].st = x; ea[i].en = y;
ea[i].value = z;
}
for(int i = 1;i <= n ;++i) father[i] = i;
sort(ea + 1,ea + m + 1,cmp);
int k = 0;
for(int i = 1;i <= m ;++i) {
if(find(ea[i].st) != find(ea[i].en)) {
unionn(ea[i].st , ea[i].en) ; k++;
add_edge(ea[i].st , ea[i].en , ea[i].value);
add_edge(ea[i].en , ea[i].st , ea[i].value);
}
if(k == n - 1) break;
}
//以上是生成树的内容啦…
for(int i = 1;i <= n ;++i)
if(!depth[i]) { //这个地方我个人理解为可能有多个并查集的意思 maybe生成树很多吧
depth[i] = 1;
dfs(i);
}
init();
int q; scanf(“%d”,&q);
while(q–) {
int x,y; scanf(“%d%d”,&x,&y);
printf(“%d\n”,lca(x , y));
}
return 0;
}
个人对这个题的理解就是
1. 这种题第一步就是要想到建两个图的问题(这已经是我第二次见这种题 感觉掌握的还可以一点)
2. 倍增lca的基本操作什么的 比如说这个题里的pre【i】【j】记录的是从i点开始 2^j步路径上的边权最小值(可能有点绕口???)