tarjan求割点 poj 1144

tarjan可真是多才多艺,既求强连通,又求割点割边,还求lca,居然每一种都相差不多。

求割点主要是利用dfs遍历树(李顶龙童鞋一直不理解tarjan,从dfs树理解应该比较直观)。

rel[x]记录x实际在树中深度,low[x]记录x及其子树在树中回边可到达的最浅深度。

如果u是割点,那么在dfs树中

1、u为根,u有一个以上子树(删除u后显然两子树不连通)。

2、u非叶子(易知叶子无关紧要),且low[u的孩子]>=rel[u](即该子树无法不通过u到达u的祖先)

下面是代码(跑不到0ms)

 

 

附缩点双联通分量

每次点入栈,把回边和父亲边加进去

hdu3686

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;
int tail[200000],next[2000000],sora[2000000],id[2000000],po[2000000];
int st[200000],f[50000][18],g[50000][18],rel[200000],low[200000],d[200000],flag[200000];
int top,n,m,ss,w_time,block,b[200000];
void link(int x,int y,int i)
{
	++ss,next[tail[x]]=ss,tail[x]=ss,sora[ss]=y,next[ss]=0,id[ss]=i;
	++ss,next[tail[y]]=ss,tail[y]=ss,sora[ss]=x,next[ss]=0,id[ss]=i;
	po[ss]=ss-1,po[ss-1]=ss;
}
void link2(int x,int y)
{
//	cout<<x<<' '<<y<<endl;
	++ss,next[tail[x]]=ss,tail[x]=ss,sora[ss]=y,next[ss]=0;
	++ss,next[tail[y]]=ss,tail[y]=ss,sora[ss]=x,next[ss]=0;
}
void dfs(int x,int y)
{
	++w_time;
	rel[x]=low[x]=w_time;
	for (int i=x,ne;next[i];) {
		i=next[i],ne=sora[i];
		if (rel[ne]) st[++top]=id[i]+n;
	}
	st[++top]=x;
	for (int i=x,ne;next[i];) {
		i=next[i],ne=sora[i];
		if (po[i]==y) continue;
		if (rel[ne]) low[x]=min(low[x],rel[ne]);
		else {
			int tmp=top;
			dfs(ne,i);
			low[x]=min(low[x],low[ne]);
			if (low[ne]>=rel[x]) {
				flag[x]=flag[x+n+n]=1;
				++block;
				//cout<<x<<' '<<top<<' '<<tmp<<endl;
				for (;top>tmp;top--) {
					b[st[top]]=block;
//					cout<<st[top]<<endl;
					if (st[top]<=n && flag[st[top]]) {
						link2(block,n+n+st[top]);
//						cout<<st[top]<<' '<<block<<endl;
					}
				}
				link2(block,n+n+x);
				top=tmp;
			}
		}
	}
}
void bfs(int s,int fl)
{
	int h,r,ne,na;
	h=r=0;
	int k=(int)(log((double)n+n+n)/log(2.0))+1;
	st[r=1]=s,rel[s]=fl,d[s]=0;
	for (;h<r;) {
		ne=st[++h];
		for (int i=ne;next[i];) {
			i=next[i],na=sora[i];
			if (!rel[na]) {
				rel[na]=fl,d[na]=d[ne]+1;
				st[++r]=na;
				f[na][0]=ne,g[na][0]=flag[na];
			}
		}
	}
	for (int j=1;j<=k;j++)
		for (int i=1;i<=r;i++) 
			f[st[i]][j]=f[f[st[i]][j-1]][j-1],
				g[st[i]][j]=g[st[i]][j-1]+g[f[st[i]][j-1]][j-1];
	///cout<<r<<endl;
	//for (int i=1;i<=r;i++) cout<<st[i]<<' '<<f[st[i]][0]<<endl;cout<<endl;
	//cout<<f[7][0]<<endl;
}
int ask(int x,int y)
{
	int e;
	//cout<<x<<' '<<y<<endl;
	if (d[x]<d[y]) e=x,x=y,y=e;
	e=d[x]-d[y];
	int ans=0;
	for (int i=0;e;e>>=1,i++)
		if (e&1) ans+=g[x][i],x=f[x][i];
	//cout<<x<<' '<<y<<endl;
	if (x==y) return ans+flag[x];
	int k=0;
	for (;;) {
		if (f[x][k]==f[y][k]) {
			if (!k) break;
			k--;
		}
		else {
			ans+=g[x][k]+g[y][k];
			x=f[x][k],y=f[y][k];
		}
	}
	ans+=g[x][k]+g[y][k];
	x=f[x][k],y=f[y][k];
	return ans+flag[x];
}
void origin()
{
	ss=n+n+n;
	for (int i=1;i<=n+n+n;i++) tail[i]=i,next[i]=0;
}
int l[200000],r[200000];
int main()
{
	freopen("input.txt","r",stdin);
	freopen("output.txt","w",stdout);
	for (;;) {
		scanf("%d%d",&n,&m);
		if (!n && !m) break;
		origin();
		for (int i=1;i<=m;i++) {
			int x,y;
			scanf("%d%d",&x,&y);
			l[i]=x,r[i]=y;
			link(x,y,i);
		}
		for (int i=1;i<=n+n+n;i++) rel[i]=low[i]=flag[i]=d[i]=0;
		for (int i=1;i<=n+m;i++) b[i]=0;
		int k=(int)(log((double)n)/log(2.0))+1;
		for (int i=1;i<=n+n+n;i++)
			for (int j=0;j<=k;j++) f[i][j]=g[i][j]=0;
		top=0,w_time=0,block=n;
		for (int i=1;i<=n;i++)
			if (!rel[i]) dfs(i,0);
	//for (int i=1;i<=n+n+n;i++) cout<<flag[i]<<' ';cout<<endl;
		//for (int i=1;i<=n+m;i++) cout<<b[i]<<' ';cout<<endl;
		for (int i=n+1;i<=n+n;i++) 
			if (!rel[i]) bfs(i,i);
		//for (int i=1;i<=n+n+n;i++) cout<<f[i][0]<<' ';cout<<endl;
		int q;
		scanf("%d",&q);
		for (int i=1;i<=n;i++)
			if (flag[i]) b[i]=i+n+n;
		for (;q;q--) {
			int x,y;
			scanf("%d%d",&x,&y);
			if ((b[x+n]==b[y+n]) || (rel[b[x+n]]!=rel[b[y+n]])) printf("0\n");
			else {
				int ans=ask(b[x+n],b[y+n]);
/*				ans=min(ans,ask(b[l[x]],b[l[y]])-flag[l[x]]);
				ans=min(ans,ask(b[r[x]],b[l[y]])-flag[r[x]]);
				ans=min(ans,ask(b[l[x]],b[r[y]])-flag[l[x]]);
				ans=min(ans,ask(b[r[x]],b[r[y]])-flag[r[x]]);*/
				printf("%d\n",ans);
			}
		}
	}
	return 0;
}


拆边为点,考虑地更少

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
#include <cmath>
using namespace std;
int ss,cnt,n,m,w_time,top,K,q;
int st[250000],rel[250000],low[250000],d[250000],tail[250000];
int b[250000],w[250000];
int f[250000][20],g[250000][20];
int next[2000000],sora[2000000];
vector <int> vec[250000];
void link(int x,int y)
{
    ++ss,next[tail[x]]=ss,tail[x]=ss,sora[ss]=y,next[ss]=0;
    ++ss,next[tail[y]]=ss,tail[y]=ss,sora[ss]=x,next[ss]=0;
}
void link2(int x,int y)
{
    vec[x].push_back(y);
    vec[y].push_back(x);
}
void dfs(int x,int y)
{
    ++w_time;
    rel[x]=low[x]=w_time;
    st[++top]=x;
    for (int i=x,ne;next[i];) {
        i=next[i],ne=sora[i];
        if (ne==y) continue;
        if (rel[ne]) low[x]=min(low[x],rel[ne]);
        else {
            int tmp=top;
            dfs(ne,x);
            low[x]=min(low[x],low[ne]);
            if (low[ne]>=rel[x]) {
                b[x]=x;
                if (x<=n) w[x]=1;
                b[++cnt]=cnt;
                link2(x,cnt);
                for (;top!=tmp;top--) {
                    int ne=st[top];
                    if (!b[ne]) b[ne]=cnt;
                    link2(ne,cnt);
                }
            }
        }
    }
}
void dfs2(int x,int s,int dis)
{
    low[x]=s;
    rel[x]=w_time;
    d[x]=dis;
    for (int i=0;i<vec[x].size();i++) {
        int ne=vec[x][i];
        if (rel[ne]!=w_time) {
            f[ne][0]=x,g[ne][0]=w[x];
            dfs2(ne,s,dis+1);
        }
    }
}
void origin()
{
    ss=n+m;
    for (int i=1;i<=n+m;i++) tail[i]=i,next[i]=0;
    for (int i=1;i<=n+m;i++) rel[i]=low[i]=0;
    w_time=0,top=0;
    for (int i=1;i<=cnt;i++) vec[i].clear();
    for (int i=1;i<=cnt;i++) w[i]=0,b[i]=0;
    cnt=n+m;
}
int ask(int x,int y)
{
    if (low[x]!=low[y]) return 0;
    if (d[x]<d[y]) swap(x,y);
    int e=d[x]-d[y];
    int sum=0;
    for (int b=0;e;e>>=1,b++)
        if (e&1) sum+=g[x][b],x=f[x][b];
    if (x==y) return sum;
    for (int i=K;i>=0;i--)
        if (f[x][i]!=f[y][i]) {
            sum+=g[x][i]+g[y][i];
            x=f[x][i],y=f[y][i];
        }
    if (x!=y) sum+=g[x][0];
    return sum;
}
int main()
{
    for (;scanf("%d%d",&n,&m)==2;) {
        if (!n && !m) break;
        origin();
        for (int i=1;i<=m;i++) {
            int x,y;
            scanf("%d%d",&x,&y);
            link(x,i+n);
            link(i+n,y);
        }
        for (int i=1;i<=n+m;i++)
            if (!rel[i]) dfs(i,0);
        ++w_time;
        for (int i=1;i<=cnt;i++) 
            if (rel[i]!=w_time) f[i][0]=0,g[i][0]=0,dfs2(i,i,0);
        K=(int)(log((double)cnt)/log(2.0))+1;
        for (int j=1;j<=K;j++)
            for (int i=1;i<=cnt;i++)
                f[i][j]=f[f[i][j-1]][j-1],
                    g[i][j]=g[i][j-1]+g[f[i][j-1]][j-1];
        scanf("%d",&q);
//        cout<<cnt<<endl;
//        cout<<f[7][0]<<' '<<f[8][0]<<' '<<f[6][0]<<endl;
        for (int i=1;i<=q;i++) {
            int x,y;
            scanf("%d%d",&x,&y);
            x+=n,y+=n;
            int ans=ask(x,y);
            printf("%d\n",ans);
        }
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Tarjan算法是一种用于解决最近公共祖先(LCA)问题的离线算法。离线算法指的是在读取所有查询之后一次性计算所有查询的答案,而不是每读取一个查询就计算一次。\[1\] 在Tarjan算法中,需要使用并查集来实现。并查集是一种数据结构,用于维护元素之间的集合关系。下面是一个并查集的模板代码: ```cpp int fa\[100000\]; void reset(){ for (int i=1;i<=100000;i++){ fa\[i\]=i; } } int getfa(int x){ return fa\[x\]==x?x:getfa(fa\[x\]); } void merge(int x,int y){ fa\[getfa(y)\]=getfa(x); } ``` 在Tarjan算法的伪代码中,首先标记当前节点为已访问状态。然后遍历当前节点的子节点,递归调用Tarjan函数并合并子节点。接下来,遍历与当前节点有查询关系的节点,如果该节点已经访问过,则输出当前节点和该节点的LCA(通过并查集的查找函数getfa获取)。\[3\] 以上是关于Tarjan算法解LCA的相关内容。 #### 引用[.reference_title] - *1* [Tarjan 算法解决 LCA 问题](https://blog.csdn.net/chengqiuming/article/details/126878817)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [详解使用 Tarjan LCA 问题(图解)](https://blog.csdn.net/weixin_34315485/article/details/93801193)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值