CEOI2017 One-Way Streets

题目描述

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

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

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

输入输出格式

输入格式:

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

接下来 m m m 行,每行两个空格隔开的正整数 a i , b i a_i,b_i ai,bi​​,表示 a i , b i a_i,b_i ai,bi​ 之间有一条边。

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

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

输出格式:

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

若第 i i i 条边必须得要是 a i a_i ai指向 b i b_i bi​ 的,那么这个字符串的第 i i i 个字符应当为 R;

若第 i i i 条边必须得要是 b i b_i bi ​ 指向 a i a_i ai ​​ 的,那么这个字符串的第 i i i个字符应当为 L;

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

思路

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

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

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

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

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

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

a = 0 a=0 a=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、付费专栏及课程。

余额充值