4839 -- Traffic Real Time Query System

本文通过一个具体的图算法案例,展示了如何使用Tarjan算法寻找强连通分量,并结合树形结构进行最近公共祖先(LCA)的高效计算。文章详细介绍了算法实现过程,包括节点遍历、子树划分等关键步骤。

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

真的没必要再在上面花时间了,交标程的事我也不想干,就把代码贴这里算了。
#pragma GCC optimize("O2")
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<ctime>
#include<cstdlib>
#include<cmath>
using namespace std;
#define rep(i,j,k) for(i=j;i<=k;++i)
#define per(i,j,k) for(i=j;i>=k;--i)
#define ll long long
#define db double
#define ldb long double
#define pii pair<int,int>
#define mkp make_pair
#define X first
#define Y second
const int N=10005,M=100005;
int n,m;
struct POINT{int K,ne;}P[M<<1],Pt[M<<1];
int he[N],tot,het[N<<1],tott;
inline void add(int x,int y){
	P[++tot]=(POINT){y,he[x]};he[x]=tot;
	P[++tot]=(POINT){x,he[y]};he[y]=tot;
}
inline void addt(int x,int y){
	Pt[++tott]=(POINT){y,het[x]};het[x]=tott;
	Pt[++tott]=(POINT){x,het[y]};het[y]=tott;
}
int dfn[N],low[N],idx,blg[M],blk,tree[N],rt[N],sec,ic[N],stk[M],Tp,vis[M<<1];
void tarjan(int x){
	int y,z,p,so=0;
	dfn[x]=low[x]=++idx;
	for(p=he[x];p;p=P[p].ne)if(!vis[p]){
		stk[++Tp]=p,vis[p]=vis[p^1]=1;
		if(!dfn[y=P[p].K]){
			tarjan(y);
			if(low[y]>=dfn[x]){
				++so;tree[++blk]=sec;
				do{blg[(z=stk[Tp--])>>1]=blk;}while(z!=p);
			}
			low[x]=min(low[x],low[y]);
		} 
		else
			low[x]=min(dfn[y],low[x]);
	}
	if(rt[sec]==x?so>1:so)
		ic[x]=++blk;
}
int pre[N<<1],son[N<<1],sz[N<<1],dad[N<<1],dep[N<<1];
void DFS1(int x,int fa){
	int y,p;sz[x]=1;dep[x]=dep[fa]+1;pre[x]=fa;
	for(p=het[x];p;p=Pt[p].ne)if(Pt[p].K!=fa){
		DFS1(y=Pt[p].K,x);sz[x]+=sz[y];
		if(sz[y]>sz[son[x]])son[x]=y;
	}
}
void DFS2(int x,int fa){
	int y,p;
	dad[x]=son[fa]==x?dad[fa]:x;
	for(p=het[x];p;p=Pt[p].ne)if(Pt[p].K!=fa)
		DFS2(y=Pt[p].K,x);
}
inline int lca(int x,int y){
	int fx=dad[x],fy=dad[y];
	while(fx!=fy)
		if(dep[fx]>dep[fy])
			fx=dad[x=pre[fx]];
		else
			fy=dad[y=pre[fy]];
	return dep[x]>dep[y]?y:x; 
}
int main(){
//	freopen("r.in","r",stdin);
//	freopen("w.out","w",stdout);
	int i,x,y,z,p,Q;int cnt=0;
	do{
		rep(i,1,n)he[i]=ic[i]=dfn[i]=0;
		rep(i,1,blk)het[i]=son[i]=0;
		rep(i,2,tot)vis[i]=0;
		idx=blk=sec=0;
		tott=tot=1;
		scanf("%d%d",&n,&m);
		if(!n&&!m)break;
		if((++cnt)>10)break;
		rep(i,1,m){
			scanf("%d%d",&x,&y);
			add(x,y);
		}
		rep(i,1,n)if(!dfn[i])tarjan(rt[++sec]=i);
		rep(i,1,n)
			if(x=ic[i])
				for(p=he[i];p;p=P[p].ne)
					addt(x,blg[p>>1]);
		rep(i,1,sec)if(he[rt[i]]){
			x=blg[he[rt[i]]>>1];
			DFS1(x,0);DFS2(x,0);
		}
		for(scanf("%d",&Q);Q--;){
			scanf("%d%d",&x,&y);
			z=lca(x=blg[x],y=blg[y]);
			printf("%d\n",dep[x]+dep[y]-(dep[z]<<1)>>1);
		}
	}while(1);
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值