zoj4097边双联通分量缩点dfs序判断

传送门:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=4097

从4月份打浙大校赛到这两天对着代码肉眼查错很久还出各种数据调试才该出来,原来还是原来的dfs序判断有问题,当时考场上写的dfs序就是由问题的,还好今天改出来了,不然就要妥协写lca了,其实lca也挺好写的,但我一直坚信dfs序判断巨对所以想把这题调出来。。。。

先边双联通分量缩点,变成一棵树,每条边都是桥,然后从任意一点dfs下去,得到dfs序数组in和out。

我们知道,v,w要到u不经过相同边,及不经过这棵树上的每一条边,于是v,w不能同时是u的父节点的其他分支,不能来自同一儿子节点的子树,不能在森林的不同树上,这样判断就行了。

关于不能来自同一儿子,我们把每个儿子的dfs序排序,然后用lower_bound找看来自第几个儿子,注意此时要按右端点排序,因为儿子节点的dfs序区间都是互不相交的,所以要按右端点比大小,找到大于等于v,w右端点的第一个右端点是哪个儿子。

判断是否来自父节点的其他分支就如果u的dfs序包含在v(w)中或与v(w)的dfs序区间不相交就是了。

上面两个判断一开始没判断全,从4月份WA到现在。。。

#include<bits/stdc++.h>
#define maxl 200010
using namespace std;

int n,m,q,cnt,ccnt,dcc,ind;
int ehead[maxl],in[maxl],out[maxl],c[maxl],f[maxl];
int cehead[maxl];
int dfn[maxl],low[maxl];
struct ed
{
	int to,nxt,id;
}e[maxl*2],ce[maxl*2];
struct node
{
	int l,r;
	bool operator < (const node &b)const
	{
		return r<b.r;
	}
	bool operator == (const node &b)const
	{
		return  r==b.r;
	}
};
vector <node> cv[maxl];
bool cut[maxl];

inline void add(int u,int v,int id)
{
	e[++cnt].to=v;e[cnt].nxt=ehead[u];e[cnt].id=id;
	ehead[u]=cnt;
}

inline void addc(int u,int v)
{
	ce[++ccnt].to=v;ce[ccnt].nxt=cehead[u];
	cehead[u]=ccnt;
}

inline void tarjan(int u,int last)
{
	dfn[u]=low[u]=++ind;
	int v;
	for(int i=ehead[u];i;i=e[i].nxt)
	{
		v=e[i].to;
		if(!dfn[v])
		{
			tarjan(v,i);
			low[u]=min(low[u],low[v]);
			if(low[v]>dfn[u])
				cut[e[i].id]=cut[e[i^1].id]=true;
		}
		else if(i!=(last^1))
			low[u]=min(low[u],dfn[v]);
	}
}

inline void dfs(int u)
{
	int v;c[u]=dcc;
	for(int i=ehead[u];i;i=e[i].nxt)
	{
		v=e[i].to;
		if(c[v] || cut[e[i].id]) continue;
		dfs(v);
	}
}

inline int find(int x)
{
	if(f[x]!=x)
		f[x]=find(f[x]);
	return f[x];
}

inline void cdfs(int u,int fa)
{
	in[u]=++ind;
	int x=find(u),y,v; 
	for(int i=cehead[u];i;i=ce[i].nxt)
	{
		v=ce[i].to;
		y=find(v);
		if(y!=x)
			f[y]=x;
		if(v==fa) continue;
		cdfs(v,u);
		cv[u].push_back(node{in[v],out[v]});
	}
	sort(cv[u].begin(),cv[u].end());
	out[u]=ind;
}

inline void prework()
{
	scanf("%d%d%d",&n,&m,&q);
	for(register int i=1;i<=n;++i)
		ehead[i]=0;
	cnt=1;int u,v;
	for(register int i=1;i<=m;++i)
	{
		scanf("%d%d",&u,&v);
		add(u,v,i);add(v,u,i);
		cut[i]=false;
	}
	for(register int i=1;i<=n;++i)
		dfn[i]=low[i]=c[i]=0;
	ind=0;dcc=0;
	for(register int i=1;i<=n;++i)
	if(!dfn[i])
		tarjan(i,0);
	for(register int i=1;i<=n;++i)
	if(!c[i])
	{
		++dcc;f[dcc]=dcc;
		dfs(i);
		in[dcc]=out[dcc]=0; 
		cehead[dcc]=0;
		cv[dcc].clear();
	}
	ccnt=1;
	for(register int i=2;i<=cnt;i+=2)
	{
		u=e[i].to;v=e[i^1].to;
		if(c[u]==c[v]) continue;
		addc(c[u],c[v]);addc(c[v],c[u]);
	}
	ind=0;
	for(register int i=1;i<=dcc;++i)
	if(!in[i])
		cdfs(i,0);
}

inline void mainwork()
{
	int u,v,w,id1,id2;bool flag;node d; 
	for(register int i=1;i<=q;++i)
	{
		scanf("%d%d%d",&u,&v,&w);
		u=c[u];v=c[v];w=c[w];
		if(find(u)!=find(v) || find(u)!=find(w))
		{
			puts("No");
			continue;
		}
		if(u==v || u==w)
		{
			puts("Yes");
			continue;
		}
		flag=false;
		if(in[v]>out[u] || out[v]<in[u] || (in[v]<=in[u] && out[v]>=out[u]))
		{
			if(in[w]>out[u] || out[w]<in[u] || (in[w]<=in[u] && out[w]>=out[u]))
				flag=false;
			else
				flag=true;
		}
		else if(in[w]>out[u] || out[w]<in[u] || (in[w]<=in[u] && out[w]>=out[u]))
			flag=true;
		 else
		{
			d.l=in[v];d.r=out[v];
			id1=lower_bound(cv[u].begin(),cv[u].end(),d)-cv[u].begin();
			d.l=in[w];d.r=out[w];
			id2=lower_bound(cv[u].begin(),cv[u].end(),d)-cv[u].begin();
			flag=(id1!=id2);
		}
		if(flag)
			puts("Yes");
		else
			puts("No"); 
	}
}

inline void print(){}

int main()
{
	int t;
	scanf("%d",&t);
	for(int i=1;i<=t;i++)
	{
		prework();
		mainwork();
		print();
	}
	return 0;
}
/*
2
6 6 6
1 2
2 3
3 1
4 5
6 4
1 4
4 1 3
1 4 2
1 2 3
1 3 3
4 2 6
2 4 5
2 1 2
1 2
1 1 1
2 1 2
*/

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值