tarjan求lca——luogu3319lca模板与1967货车运输

本文介绍了一种利用Tarjan算法与并查集相结合的方法来解决最近公共祖先(LCA)问题,并通过实例展示了如何在线下环境中实现该算法。此外,还探讨了将此方法应用于货车运输场景中的具体实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

lca的tarjan求法,与并查集结合,离线完成

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<vector>
const int maxn=500009;
using namespace std;
struct node{
    int v,id,next;
    node(int a=0,int b=0,int c=0):v(a),id(b),next(c){}
}edge[2*maxn],ques[2*maxn];


int	n,m,s,anslca[maxn],fa[maxn],cnt=0;
int head[maxn],qhead[maxn];
bool vis[maxn];
int add(node e[],int hd[],int u,int v,int w){
    e[++cnt]={v,w,hd[u]};
    hd[u]=cnt;
    e[++cnt]={u,w,hd[v]};
    hd[v]=cnt;
}
int find(int x) {
    if(fa[x]==x)return x;
    else return fa[x]=find(fa[x]) ;
}
void tarlca(int u) {
    fa[u]=u; vis[u]=1;
    for(int i=head[u]; i>0; i=edge[i].next) {
        int v=edge[i].v;
        if(!fa[v]){tarlca(v);fa[v]=u;}
    }
    
    for(int i=qhead[u];i>0;i=ques[i].next){
        int v=ques[i].v,id=ques[i].id;
        if(vis[v]){
        	anslca[id]=find(v);
        }
    }
}
int main(){


    memset(head,0,sizeof(head));
    memset(qhead,0,sizeof(qhead));
    int a,b;
    scanf("%d%d%d",&n,&m,&s);
    for(int i=1;i<n;i++){
        scanf("%d%d",&a,&b);
        add(edge,head,a,b,0);
    }
    cnt=0;
    for(int i=0;i<m;i++){
        scanf("%d%d",&a,&b);
       	add(ques,qhead,a,b,i);
    }
    tarlca(s);
    for(int i=0;i<m;i++)cout<<anslca[i]<<endl; 
    
} 



又写一遍货车运输

并查集和搜索的结合

有一个坑点,路径上的值可能为0,那么最小值就是0啦,可是要和不能到达一样,要输出-1.不知道是代码的问题还是程序的问题。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
using namespace std;
const int MaxN=10005,MaxM=100005,inf=0x3f3f3f3f;
struct LinkType {
	int a,b,c;
};
LinkType w[MaxM],e[MaxM],q[MaxM];
int he[MaxN],hq[MaxN];
int father[MaxN],fak[MaxN],mv[MaxN],mark[MaxN],ans[MaxM];
int ance[MaxN];
int N,M,Q,e0=0,q0=0;
void Addq(int x,int y,int z)
//hr[]存查询的起点,q[].a查询的终点,q[].c对应的输入序号  .b前向星链接
{
	q0++;
	q[q0].a=y;
	q[q0].b=hq[x];
	hq[x]=q0;
	q[q0].c=z;

}
void Adde(int x,int y,int z)
//最小生成树加边
{
	e0++;
	e[e0].a=y;
	e[e0].b=he[x];
	he[x]=e0;
	e[e0].c=z;
}

void Read() {
	int i;
	scanf("%d%d",&N,&M);
	for(i=1; i<=M; i++) scanf("%d%d%d",&w[i].a,&w[i].b,&w[i].c);
	
}
bool cmp(LinkType a,LinkType b) {
	return a.c>b.c;
}

int Find(int x) {
	int fa;
	if(father[x]==x) return x;
	fa=Find(father[x]);
	mv[x]=min(mv[x],mv[father[x]]);//找到当前子集合的路径上的最大值
	return father[x]=fa;
}
void Rebuild() { //求最小生成树,并建立树,存储在数组e.
	int i,r1,r2;
	for(i=1; i<=N; i++)father[i]=i;
	sort(w+1,w+1+M,cmp);
	for(i=1; i<=M; i++) {
		r1=Find(w[i].a);
		r2=Find(w[i].b);
		if(r1!=r2) {
			father[r2]=r1;
			Adde(r1,r2,w[i].c);
			Adde(r2,r1,w[i].c);
		}
	}
	for(int i=1;i<=N;i++)Find(i);
	memcpy(fak,father,sizeof(father)) ;//原来的并查集还用的着。 
}
void Tarjan(int x,int fa) {
	int i,y,r1,r2;
	father[x]=x;
	mv[x]=inf;//最大过路费 
	//	ance[x]=x;
	for(i=he[x]; i; i=e[i].b) { //前向星找到当前节点的孩子,递归
		y=e[i].a;
		if(y==fa)continue;
		Tarjan(y,x);
		father[y]=x;//合并集合
		mv[y]=e[i].c;
	//	ance[Find(x)]=x;
	}
	mark[x]=1;

	for(i=hq[x]; i; i=q[i].b) { //处理与x相关的查询,的序号
		y=q[i].a;//找到与x相连的y, 找到y所在的集合序号,添加上其查输入的次序
		Find(y);//找到当前节点到子树祖先路径上的最小值。
		if(mark[y]) {
			// cout<<"  ance x-y:"<<x<<"-"<<y <<"="<<ance[Find(y)]<<"  max"<<min(mv[x],mv[y])<<"  输入序号"<<q[i].c<<endl;
			ans[q[i].c]=min(mv[x],mv[y]);
		}
	}

}
void Solve() {
	Rebuild();	
	scanf("%d",&Q);int a,b;
	for(int i=1; i<=Q; i++) { //tarjan,查询队列
		scanf("%d%d",&a,&b);
		if(fak[a]==fak[b]) {
			Addq(a,b,i);//加边,起点,存hq[],终点q[].a,序号 q[].c
			Addq(b,a,i);
		}
	}
	for(int i=1;i<=N;i++)if(!mark[i])Tarjan(i,0);
	for(int i=1; i<=Q; i++){
		if(ans[i]==inf||ans[i]==0)ans[i]=-1;
		printf("%d\n",ans[i]);
	}
}
int main() {
	Read();
	Solve();
	return 0;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值