tarjian的各种板子=。=

有向图的强连通分量,缩点a!

无向图求割点a! 求桥a!

求lca!

复杂度是O(n+m)

有向图的强连通分量板子

 void tarjian(int x)
 {
 	dfn[x] = low[x] = ++idc;
 	vis[x] = 1;
 	zhan[++top] = x;
 	for(int i = h[x]; i; i = nex[i])
 	{
 		int v = tov[i];
 		if(dfn[v] == 0)
 		{
 			tarjian(v);
 			low[x] = min(low[x],low[v]);
         }
         else if(vis[v] == 1)
         {
         	low[x] = min(low[x],dfn[v]);
         }
     }
     if(dfn[x] == low[x])
     {
     	seq++;
     	while(1)
     	{
     		int u = zhan[top];
     		top--;
     		tow1[seq] += tow[u];
     		color[u] = seq;
     		vis[u] = 0;
     		if(u == x) break;
         }
     }
 }
 for(int i = 1; i <= m; i++)
    {
        if(color[ha[i]] != color[he[i]])
        { 
            add(color[ha[i]],color[he[i]]);
        }
    }//缩点
void tarjian(int x,int fa)//割点
{
	int child = 0;
	dfn[x] = low[x] = ++idc;
	for(int i = h[x]; i ;i = nex[i])
	{
		int v = tov[i];
		if(v == fa) continue;
		if(dfn[v] == 0)
		{	
		     child++;
			tarjian(v,x);
			if(low[v] >=  dfn[x] &&fa != -1)
			{
				if(vis1[x] == 0) {
						vis1[x] = 1;
				cnt++;
				}			
		    }
		    low[x] = min(low[v],low[x]);
		}
		else
		{
		low[x] = min(dfn[v],low[x]);
		}
	
	}
	if(fa == -1 && child >= 2)
	{
		if(vis1[x] == 0)
	{vis1[x] = 1;
		cnt++;}
		
	}
}//只要有一个儿子无法回到它的祖先,它就是割点,根节点有两个及以上儿子则是

void tarjian(int x,int fa)//无重边 
{
	int child = 0;
	dfn[x] = low[x] = ++idc;
	for(int i = h[x]; i ;i = nex[i])
	{
		int v = tov[i];
		if(v == fa) continue;
		if(dfn[v] == 0)
		{	
		     child++;
			tarjian(v,x);
			if(low[v] >  dfn[x])
			{
				vis1[i] = 1;
				cnt++;		
		    }
		    low[x] = min(low[v],low[x]);
		}
		else
		{
		low[x] = min(dfn[v],low[x]);
		}
	}
}//只要有一个儿子无法回到它的祖先和它,则这条边是桥 ,此为无重边=、=
//然而现实是残酷的,往往有重边,它可以通过除了它来的这条边之外的边回到父亲,所以不能只要v == fa
//就回去=、=
 void tarjian(int x,int id)//有重边 
{
	int child = 0;
	dfn[x] = low[x] = ++idc;
	for(int i = h[x]; i ;i = nex[i])
	{	
	    if(i == id/2) continue;
		int v = tov[i];
	    if(dfn[v] == 0)
		{	
		     child++;
			tarjian(v,i);
			if(low[v] >  dfn[x])
			{
				vis1[i] = 1;
				cnt++;		
		    }
		    low[x] = min(low[v],low[x]);
		}
		else
		{
		low[x] = min(dfn[v],low[x]);
		}
	}
}//所以记它是从哪条边来的,只要 i == id/2说明这和来的边是一条边=。=、 

求lca

一种离线算法

对于询问(u, v)

if v在u的子树中那么lca为u

if v不在u的子树中那么lca为u

v有可能在u爸爸的子树中,如果不在,v有可能在u爸爸的爸爸的子树中

//类似于并查集

#include<iostream>//这是 P3379 【模板】最近公共祖先(LCA) 
#include<cstdio>
using namespace std;
int nex[1000005], tp, h[1000005], tov[1000005];
int tpp,nexx[1000005],hh[1000005],tovv[1000005];
int n,m,s,fa[1000005],tow[1000005],ans[1000005];
void add(int x,int y)
{
	tp++;
	nex[tp] = h[x];
	h[x]  = tp;
	tov[tp] = y; 
}
void  add1(int x,int y,int w)
{
	tpp++;
	nexx[tpp] = hh[x];
	hh[x]  = tpp;
	tovv[tpp] = y;
	tow[tpp] = w;
}
int find(int x)
{
	if(x == fa[x]) return x;
	return fa[x] = find(fa[x]);
}
void tarjian(int x)
{
	fa[x]  = x;//假设处理以x为根的一棵小子树 ; 
	for(int i = h[x]; i ; i = nex[i])
	{
		int v = tov[i];
		if(fa[v] == 0) 
		{
		     tarjian(v);
			 fa[v] = x;	
		}
	}
	for(int i = hh[x]; i ; i = nexx[i])
	{
		int v = tovv[i];
		if(fa[v] != 0&&ans[tow[i]] == 0)
		{
			ans[tow[i]] = find(v);//u = Lca(x,v)如果u = fa[u],说明 x,v在 u的子树中
			//从v一直往上找,找到的第一个 u(满足u = fa[u]) ,说明 u = Lca(x,v) 
		}
	}
}
int main()
{
	cin >> n >> m >> s;
	for(int i = 1; i <= n - 1; i++)
	{
		int x,y;
		scanf("%d%d", &x,&y);
		add(x,y);
		add(y,x);
	}
	for(int i = 1; i <= m; i++)
	{
		int x,y;
		scanf("%d%d", &x,&y);
		add1(x,y,i);
		add1(y,x,i);
	}
	tarjian(s);
	for(int i= 1; i <= m; i++)
	{
		cout << ans[i] << endl;
	}
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值