CEOI2017 One-Way Streets

本文探讨了如何在给定限制条件下,将无向图转化为有向图的问题。通过使用双联通分量和森林结构,文章详细解释了如何确定每条边的方向,并提供了实现这一过程的代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述

给定一张 nnn 个点 mmm 条边的无向图,现在想要把这张图定向。

ppp 个限制条件,每个条件形如 (xi,yi)(x_i,y_i)(xi,yi),表示在新的有向图当中,xix_ixi 要能够沿着一些边走到 yiy_iyi ​​。

现在请你求出,每条边的方向是否能够唯一确定。同时请给出这些能够唯一确定的边的方向。

输入输出格式

输入格式:

第一行两个空格隔开的正整数 n,mn,mn,m

接下来 mmm 行,每行两个空格隔开的正整数 ai,bia_i,b_iai,bi​​,表示 ai,bia_i,b_iai,bi​ 之间有一条边。

接下来一行一个整数 ppp,表示限制条件的个数。

接下来 ppp 行,每行两个空格隔开的正整数 xi,yix_i,y_ixi,yi,描述一个 (xi,yi)(x_i,y_i)(xi,yi)的限制条件。

输出格式:

输出一行一个长度为 mmm 的字符串,表示每条边的答案:

若第 iii 条边必须得要是 aia_iai指向 bib_ibi​ 的,那么这个字符串的第 iii 个字符应当为 R;

若第 iii 条边必须得要是 bib_ibi ​ 指向 aia_iai ​​ 的,那么这个字符串的第 iii个字符应当为 L;

否则,若第 iii 条边的方向无法唯一确定,那么这个字符串的第 iii 个字符应当为 B。

思路

可以得到一个的结论:在同一个双联通分量中的点之间的边方向无法确定。

所以可以缩点,然后得到一个森林。

对于要求的方向,多定义一个aaa数组,对于起点xxxa[x]++a[x]++a[x]++,终点yyya[y]−−a[y]--a[y]

每一点走向父节点的边方向可以记住aaa来判断。

a&lt;0a&lt;0a<0则应由父节点走向子节点

a&gt;0a&gt;0a>0则应由子节点走向父节点

a=0a=0a=0则无法判断方向。

代码

#include<cstdio>
#include<string>
#include<cstring>
#define R_ register
inline int read() {
	int ret=0,f=1,ch=getchar();
	for (; !isdigit(ch); ch=getchar()) if (ch=='-') f=-f;
	for (; isdigit(ch); ch=getchar()) ret=ret*10+ch-48;
	return ret*f;
}
const int maxn=2e5+5;
struct edge {int son,nxt,id,s;} E[maxn],T[maxn];
int top,tar,cnt,sta[maxn],dfn[maxn],low[maxn],bel[maxn];
int N,M,P,tot=1,lnk[maxn],hed[maxn],Ans[maxn],vis[maxn],a[maxn];
inline void add_edge(int y,int x,int id) {
	E[++tot].son=y,E[tot].nxt=lnk[x],lnk[x]=tot,E[tot].id=id,E[tot].s=+1;
	E[++tot].son=x,E[tot].nxt=lnk[y],lnk[y]=tot,E[tot].id=id,E[tot].s=-1;
}
inline void Add_edge(int x,int y,int id,int s) {
	T[++tot].son=y,T[tot].nxt=hed[x],hed[x]=tot,T[tot].id=id,T[tot].s=s;
}
void tarjan(int x,int pre=0) {
	dfn[x]=low[x]=++cnt,sta[++top]=x;
	for (R_ int k=lnk[x],v; v=E[k].son,k; k=E[k].nxt) if (k^pre)
		if (!dfn[v]) {
			tarjan(v,k^1),low[x]=std::min(low[x],low[v]);
			if (low[v]>dfn[x]) {
				for (++tar; sta[top]^v; ) bel[sta[top]]=tar,--top;
				bel[sta[top]]=tar,--top;
			}
		} else low[x]=std::min(low[x],dfn[v]);
}
void dfs(int x,int fa,int id,int s) {
	for (R_ int k=hed[x],v; v=T[k].son,k; k=T[k].nxt) if (!vis[v])
		vis[v]=1,dfs(v,x,T[k].id,T[k].s),a[x]+=a[v];
	if (a[x]) Ans[id]=(a[x]*s<0?1:2);
}
int main() {
	R_ int i,k,x,y;
	for (N=read(),M=read(),i=1; i<=M; ++i) add_edge(read(),read(),i);
	for (i=1; i<=N; ++i) if (!dfn[i]) tarjan(i);
	if (top) for (++tar; top; --top) bel[sta[top]]=tar;
	for (tot=i=1; x=bel[i],i<=N; ++i)
		for (k=lnk[i]; y=bel[E[k].son],k; k=E[k].nxt)
			if (x^y) Add_edge(x,y,E[k].id,E[k].s);
	for (P=read(),i=1; i<=P; ++i) ++a[bel[read()]],--a[bel[read()]];
	for (i=1; i<=tar; ++i) if (!vis[i]) vis[i]=1,dfs(i,0,0,0);
	for (i=1; i<=M; ++i) putchar(!Ans[i]?'B':(Ans[i]==1?'R':'L'));
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值