您的位置:首页 > 其它

UVALive5796点双联通分量or边双联通分量

2013-10-20 20:05 369 查看
两种方法都可以。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <stack>
#define N 10010
#define M 100100
#define Q 1010
using namespace std;

struct Edge{
	int v,next;
}edge[M*2 + N*4];
int head
,cnt;
int dfn
,low
,color
,depth,b,c;
int cut
;
int num
;
vector<int>dpt
;
vector<int>node_bcc
;
stack<int>ss;

int head2[N*2];
int mark[N*2];

int sum[N*2];
int f[N*2],flag[N*2];
bool ok[N*2];
struct Query{
	int v,id,next;
}query[Q*2];
int head3[N*2],cnt3;
int ans[Q];

void init(){
	memset(head,-1,sizeof(head));
	memset(head2,-1,sizeof(head2));
	memset(head3,-1,sizeof(head3));
	memset(dfn,0,sizeof(dfn));
	memset(cut,0,sizeof(cut));
	memset(color,0,sizeof(color));
	memset(sum,0,sizeof(sum));
	memset(flag,0,sizeof(flag));
	cnt=depth=c=cnt3=0;
	for(int i=0;i<N;i++) dpt[i].clear(),node_bcc[i].clear();
}

void addedge(int u,int v,int *head){
	edge[cnt].v=v;
	edge[cnt].next=head[u];
	head[u]=cnt++;
	edge[cnt].v=u;
	edge[cnt].next=head[v];
	head[v]=cnt++;
}

void addedge3(int u,int v,int id){
	query[cnt3].v=v;
	query[cnt3].id=id;
	query[cnt3].next=head3[u];
	head3[u]=cnt3++;
	query[cnt3].v=u;
	query[cnt3].id=id;
	query[cnt3].next=head3[v];
	head3[v]=cnt3++;
}

void tarjan(int u,int fa,int cou){
	dfn[u]=low[u]=++depth;
	num[u]=cou;
	for(int i=head[u];i!=-1;i=edge[i].next){
		int v=edge[i].v;
		if(v==fa) continue;
		if(dfn[v]==0){
			ss.push(i);
			tarjan(v,u,cou);
			low[u]=min(low[u],low[v]);
			if(dfn[u]<=low[v]){
				cut[u]++;
				c++;
				int j;
				do{
					j=ss.top();
					ss.pop();
					if(color[edge[j].v]!=c){
						color[edge[j].v]=c;
						dpt[c].push_back(edge[j].v);
						node_bcc[edge[j].v].push_back(c);
					}
					if(color[edge[j^1].v]!=c){
						color[edge[j^1].v]=c;
						dpt[c].push_back(edge[j^1].v);
						node_bcc[edge[j^1].v].push_back(c);
					}
				}while(j!=i);
			}
		}
		else{
			low[u]=min(low[u],dfn[v]);
			if(dfn[u]>dfn[v]) ss.push(i);
		}
	}
}

void dfs(int u,int fa){
	ok[u]=1;
	for(int i=head2[u];i!=-1;i=edge[i].next){
		int v=edge[i].v;
		if(v==fa) continue;
		sum[v]=sum[u]+mark[v];
		dfs(v,u);
	}
}

int find(int u){
	if(f[u]==u)return u;
	return f[u]=find(f[u]);
}

void tarjan_lca(int u,int fa){
	ok[u]=1;
	f[u]=u;
	for(int i=head2[u];i!=-1;i=edge[i].next){
		int v=edge[i].v;
		if(v==fa) continue;
		tarjan_lca(v,u);
		f[v]=u;
	}
	flag[u]=1;
	for(int i=head3[u];i!=-1;i=query[i].next){
		int v=query[i].v;
		if(!flag[v]) continue;
		int t=find(v);
		ans[query[i].id]=sum[u]+sum[v]-2*sum[t]+mark[t];
	}
}

int main(){
	int n,m,q,u,v;
	while(scanf("%d %d %d",&n,&m,&q)){
		if(n==0 && m==0 && q==0) break;
		init();
		for(int i=1;i<=m;i++){
			scanf("%d %d",&u,&v);
			addedge(u,v,head);
		}
		int cou=0;
		for(int i=1;i<=n;i++){
			if(!dfn[i]){
				tarjan(i,0,++cou);
				cut[i]--;
			}
		}

		b=c;
		for(int i=1;i<=n;i++) if(cut[i]>0) color[i]=++c; else if(cut[i]==0) color[i]=node_bcc[i][0];
		for(int i=1;i<=n;i++) if(cut[i]==-1) color[i]=++c;
		//b表示点双联通分量个数,编号1~b,然后从b+1开始编号所有的割点,最后编号所有的孤立点,color数组存放原图中的点对应新图中的编号,割点和孤立的点其值为单独的编号,剩余的点其值为对应点双联通分量编号

		for(int i=1;i<=b;i++){
			for(int j=0;j<dpt[i].size();j++){
				u=dpt[i][j];
				if(cut[u]){
					addedge(color[u],i,head2);
				}
			}
		}

		for(int i=1;i<=b;i++){
			if(dpt[i].size()>2) mark[i]=1;
			else mark[i]=0;
		}
		for(int i=b+1;i<=c;i++) mark[i]=0;
		//mark数组标记这个双联通分量中的点的个数是否大于3,如果是的话如果通过这个点,那一定会有多条简单路径,接下来就是对缩点成树就行判断,某一对点之间的路径上是否存在mark值为1的点,方法有很多,我用的是LCA,也可以把这些点删去后在原图上判断连通性
		for(int i=1;i<=q;i++){
			int a,b;
			scanf("%d%d",&a,&b);
			if(num[a]!=num[b]) ans[i]=100;
			else addedge3(color[a],color[b],i);
		}

		memset(ok,0,sizeof(ok));
		for(int i=1;i<=c;i++){
			if(!ok[i]){
				sum[i]=mark[i];
				dfs(i,0);
			}
		}
		memset(ok,0,sizeof(ok));
		for(int i=1;i<=c;i++){
			if(!ok[i]){
				tarjan_lca(i,0);
			}
		}

		for(int i=1;i<=q;i++)
			if(ans[i])puts("N");
			else puts("Y");
		puts("-");
	}
	return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: