您的位置:首页 > 其它

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

2018-01-04 17:38 375 查看
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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: