逃不掉的路’s 题解

这个题解想写但拖了好久了。。。

emmm…这算是一道模板题吧,但当初没多少人过了,然后就决定来写篇题解,毕竟也好久没写了。。。

(本题来自HLOJ#1578)

呃,题目就是给你一张图,然后求a节点到b节点必须经过几条边。

这题呢可能会出现两个点有两条不同的路径,显然这两点组成了一个环。那么我们思考环的特性。

显然环是没有割边与割点的。由此想到Tarjan算法求联通分量。

这样就进行了缩点,使得题中的图变成了一棵树。

而一定经过的边即是割边,所以再求割边数即可。

于是就建图+缩点+lca+两个dfs,结束。。。

感觉好快。。。

再贴个代码帮助理解(表示懒得去掉宏定义,所以。。。见谅)

#include<bits/stdc++.h>
using namespace std;
#define s1(a,n) sort(a+1,a+n+1)
#define s2(a,n) sort(a+1,a+n+1,mc)
#define ll long long
#define sg string
#define st struct
#define db double
#define f1(i,l,r) for(int i=l;i<=r;++i)
#define f2(i,r,l) for(int i=r;i>=l;--i)
#define f3(i,a,b) for(int i=a;i;i=b)
#define me(a,b) memset(a,b,sizeof(a))
#define sf scanf
#define pf printf
#define fi(a) freopen(a,"r",stdin)
#define fo(a) freopen(a,"w",stdout)
#define cn continue
#define bk break
#define rt return
#define ct const
ct int maxn=100000+10;
ct int maxm=200000+10;
int l,r,n,m,q,k,s=0,s1=0,t=0,num=0,ans=0,d[maxn],f[maxn][25],dfn[maxn],low[maxn],c[maxm],c1[maxn],vis[maxn],Top[maxn];
st lol{
	int x,y,z;
}e[maxm*2];
st dota{
	int a,b;
}e1[maxn*2];
bool mc(int x,int y){
	rt x>y;
}
void ein(int u,int v,int w){
	++ans;
	e[ans].y=v;
	e[ans].z=w;
	e[ans].x=Top[u];
	Top[u]=ans;
}
void init(){
	sf("%d%d",&n,&m);
	f1(i,1,m){
		sf("%d%d",&e1[i].a,&e1[i].b);
		ein(e1[i].a,e1[i].b,i);
		ein(e1[i].b,e1[i].a,i);
	}
}
void gg(int x,int y){
	++num;
	dfn[x]=low[x]=num;
	f3(i,Top[x],e[i].x){
		if(dfn[e[i].y]){
			if(e[i].z!=y)
				low[x]=min(low[x],dfn[e[i].y]);
			cn;
		}
		gg(e[i].y,e[i].z);
		low[x]=min(low[x],low[e[i].y]);
		if(dfn[x]<low[e[i].y])
			++c[e[i].z];
	}
}
void hh(int x){
	c1[x]=s;
	f3(i,Top[x],e[i].x)
		if(!c[e[i].z]&&!c1[e[i].y])
			hh(e[i].y);
}
void kk(int x){
	vis[x]=1;
	f3(i,Top[x],e[i].x){
		if(vis[e[i].y]) cn;
		f[e[i].y][0]=x;
		d[e[i].y]=d[x]+1;
		kk(e[i].y);
	}
}
int ee(int x,int y){
	int ss=0,s2;
	if(d[x]>d[y]) swap(x,y);
	for(int i=0,t=d[y]-d[x];t;t>>=1,++i)
		if(t&1)
			y=f[y][i];
	if(x==y) rt x;
	s2=s;
	while(s2){
		++ss;
		s2/=2;
	}
	f2(i,ss,0)
		if(f[x][i]!=f[y][i]){
			x=f[x][i];
			y=f[y][i];
		}
	rt f[x][0];
}
void work(){
	gg(1,0);
	f1(i,1,n)
		if(!c1[i]){
			++s;
			hh(i);
		}
	me(Top,0);
	ans=0;
	f1(i,1,m)
		if(c1[e1[i].a]!=c1[e1[i].b]){
			ein(c1[e1[i].a],c1[e1[i].b],i);
			ein(c1[e1[i].b],c1[e1[i].a],i);
		}
	kk(1);
	for(int i=1;i<<i<s;i++)
		f1(j,1,s)
			if(f[j][i-1])
				f[j][i]=f[f[j][i-1]][i-1];
}
void prin(){
	sf("%d",&q);
	for(int i=1;i<=q;i++){
		sf("%d%d",&l,&r);
		k=ee(c1[l],c1[r]);
		pf("%d\n",d[c1[l]]+d[c1[r]]-2*d[k]);
	}
}
int main(){
	fi("test.in");
	fo("test.out");
	init();
	work();
	prin();
	rt 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值