poj 3694(求边双连通分量 缩点 暴力LCA)

题意:给你一个无向连通图,让后给出每给q条要添加的边,输出添加每一条边之后图中还剩下多少桥


分析:向图中加入边那么就有可能是得图中的桥减小,那么我们可以先求出图中的双连通分量,那么若添加的一条边是属于某个双连通分量的那么桥的条数没有减少,如果这条边的两个顶点分别属于两个双连通子图,那么从就会使桥的数目减小,那么问题就分为两步来解决: 1、求出双连通分量后和缩点 2、加入一条边后求出桥减少的条数


第一步很好解决,这里就不赘述!

第二步:因为缩点之后新图就是一颗树,向该树中添加一条边,那么树中就会形成一条环(就形成了一个双连通分量),那么这个环上的边(当然不包含添加的那条边)就是减少的桥的条数,那么我们现在的任务就是找出加边之后形成的环中的边,那么我们可以用暴力lca(具体的见代码)


代码:


#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;

const int N = 100005;
const int E = 400005;

struct node
{
	int x,y,nxt;
}edge[E],edge1[E];
int e, head[N];
int e1,first[N];
int cnt;
int Min(int a,int b)
{
	return a>b?b:a;
}
void addedge(int x, int y)
{
	edge[e].x=x;
	edge[e].y=y;
	edge[e].nxt=head[x];
	head[x]=e++;

	edge[e].x=y;
	edge[e].y=x;
	edge[e].nxt=head[y];
	head[y]=e++;
}

void add(int x,int y)
{
	edge1[e1].x=x;
	edge1[e1].y=y;
	edge1[e1].nxt=first[x];
	first[x]=e1++;
}

int dfn[N],low[N],index,top,st[N],blg[N];
void Cutedge(int u, int fa)
{
	int v,x;
	low[u]=dfn[u]=index++;
	st[++top]=u;
	bool tag=false;
	for(int i=head[u];i!=-1;i=edge[i].nxt)
	{
	     v=edge[i].y;
		if(v==fa&&tag==false){tag=true;continue;}//处理重边
		if(dfn[v]==-1)
		{
			Cutedge(v,u);
		   low[u]=Min(low[u],low[v]);
		   if(low[v]>dfn[u])
		   {
			   cnt++;
			   do
			   {
				   x=st[top];
				   top--;
				   blg[x]=cnt;
			   }while(x!=v);
		   }

		}
		else low[u]=Min(low[u],dfn[v]);
	}
	
}
int pre[N],father[N];
void dfs(int u, int fa)
{
	//pre[u]=fa;
	for(int i=first[u];i!=-1;i=edge1[i].nxt)
	{
		int v = edge1[i].y;
		if(fa==v)continue;
		pre[v]=u;
		dfs(v,u);
	}

}

int tag[N];
bool vis[N];
int lca(int x, int y,int c)
{
	int num,ans;
	if(x==y)
		return 0;
	tag[x]=c;tag[y]=c;
	int x1=x;
	int y1=y;
    while(1)
	{
        if(x1!=pre[x1])
        {
			x1=pre[x1];
			if(tag[x1]==c){ans=x1;break;}
	        tag[x1]=c;
		}
	
		if(y1!=pre[y1])
		{
			y1=pre[y1];
			if(tag[y1]==c){ans=y1;break;}
            tag[y1]=c;
		}
	}
   num=0;
   while(x!=ans)
   {
	   if(vis[x]==0)
	   {vis[x]=1;num++;}
	   x=pre[x];
   }
   while(y!=ans)
   {
	   if(vis[y]==0)
	   {vis[y]=1;num++;}
	   y=pre[y];
   }
   return num;
}



int main ()
{
	int n, m,k=1;
	int i,x,y,q;
	while(scanf("%d%d",&n,&m)!=EOF)
	{
		if(n==0&&m==0)break;
		e = 0;
		memset(head,-1,sizeof(head));
		for(i=0;i<m;i++)
		{
			scanf("%d%d",&x,&y);
			addedge(x,y);
		}
		memset(dfn,-1,sizeof(dfn));
		memset(low,0,sizeof(low));
		 index=1;
		 top=0;
		 cnt=0;
		Cutedge(1,-1);//求割边且缩点
		cnt++;
		while(top!=0)
		{
			blg[st[top]]=cnt;
			top--;
		}
		e1=0;	
		memset(first,-1,sizeof(first));
		for(i=0;i<e;i++)//缩点建图
		{
			x=blg[edge[i].x];
			y=blg[edge[i].y];
			if(x==y)continue;
			add(x,y);
		}
/*	for(i=1;i<=cnt;i++)
			for(int j=first[i];j!=-1;j=edge1[j].nxt)
				printf("%d %d \n",i,edge1[j].y);*/
		dfs(1,1);
	//	for(i=1;i<=cnt;i++)
	//		printf("%d ",pre[i]);
	//	cout << endl;
		cnt--;
		scanf("%d",&q);
		printf("Case %d:\n",k++);
		memset(vis,0,sizeof(vis));
		memset(tag,0,sizeof(tag));
		for(i=1;i<=q;i++)
		{
			scanf("%d%d",&x,&y);
			x=blg[x];
		    y=blg[y];
		//	cout << x<<" " << y << endl;
			int num=lca(x,y,i);
          printf("%d\n",cnt-num);
		  cnt-=num;
		 
		}
		printf("\n");
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值