[NOIP模拟赛]单向道路

30 篇文章 0 订阅
13 篇文章 0 订阅
题目描述
一个国家有n个城市和m个双向道路连接他们。技术发展导致道路上行驶的车辆越来越多,越来越快,这导致出现问题:两辆相反方向行驶车辆的道路变得太窄。解决这一问题的方法是将所有道路转为单向道路。

把道路改为单向会付出一些代价,例如以前可达的那些城市中的一些可能在更改后不再可达。政府编制了一系列重要的城市对,必须从第一个城市开始,到达第二个城市。你的任务是确定每条道路的方向,确保存在解决方案使得重要的城市对满足条件。对于某些道路,如果您想要获得解决方案,则无法选择道路的方向。车辆必须从第一个城市开向第二个城市(规定为向右的方向,由字母R表示),或者必须从第二个城市开向第一个(规定为向左的方向,用字母L表示)。然而,对于某些道路,存在某一个解决方案使得它的方向为左,而在另一个(可能是不同的)解决方案中它的方向为右。这时用两个方向的字母B来表示这些道路。


输入格式
第1行:2个整数表示n和m(1≤n,m,p ≤100000)
接下来m行,每行2个整数ai和bi,表示在城市ai和bi之间存在一条道路。在一对城市之间可能存在多条道路,而且一条道路可能连接同一个城市。 

接下来1行:1个整数p表示重要城市对的数量接下来p行,每行2个整数xi和yi,表示必须能够从xi出发,走到yi(1≤ai,bi,xi,yi≤n)


输出格式
输出一个长度为m的字符串,第i个字符应当为:
R 如果所有的解决方案中第i条道路都必须向右
L 如果所有的解决方案中第i条道路都必须向左

B 如果某一个解决方案中第i条道路向左,而在另一个解决方案中它的方向为右


输入样例
5 6
1 2
1 2
4 3
2 3
1 3
5 1
2
4 5

1 3


输出样例

BBRBBL


样例说明

以第5条道路"1 3"为例,两个满足条件的解决方案是:LLRLRL 和RLRRLL,所以它的方向为B



题解:缩点+LCA
首先在环中的边一定是B。因此将环缩为点,使原图变为一棵树。再看重要城市对是否是同一个点,不是就找其LCA,标记重要城市对到LCA的路径。

思路很简单,就是代码量有点大。


#include<cstdio>
#include<algorithm>
using namespace std;
const int N=100005;
const int M=N<<1;

void Getin( int &shu ) {
	char c; int f=1; shu=0;
	for( c=getchar(); c<'0' || c>'9'; c=getchar() ) if( c=='-' ) f=-1;
	for( ; c>='0' && c<='9'; c=getchar() ) shu=shu*10+c-'0';
	shu*=f;
}


int fir[N], ecnt=1;
struct enode{ int e, next; bool flg; } edge[M];
void Elink( int s, int e ) {
	edge[++ecnt].e=e; edge[ecnt].next=fir[s]; fir[s]=ecnt;
	edge[++ecnt].e=s; edge[ecnt].next=fir[e]; fir[e]=ecnt;
}

int n, m, s, e;
void Build_map() {
	Getin(n); Getin(m);
	for( int i=1; i<=m; i++ ) {
		Getin(s); Getin(e);
		if( s==e ) { ecnt+=2; continue; }
		Elink( s, e );
	}
}


int dfn[N], low[N], dfs_clock, pcnt;
bool bri[M], vis[N];
void Tarjan( int r, int laste ) {//找桥
	dfn[r]=low[r]=++dfs_clock;
	for( int i=fir[r]; i; i=edge[i].next )
		if( !dfn[ edge[i].e ] ) {
			Tarjan( edge[i].e, i );
			low[r]=min( low[r], low[ edge[i].e ] );
			if( dfn[r]<low[ edge[i].e ] ) bri[i]=bri[i^1]=1;
		}
		else if( i!=(laste^1) )
			low[r]=min( low[r], dfn[ edge[i].e ] );
}

void Note( int r, int id ) {
    low[r]=id; vis[r]=1;
    for( int i=fir[r]; i; i=edge[i].next )
        if( !vis[ edge[i].e ] ) {
			if( bri[i] ) Note( edge[i].e, ++pcnt );
			else Note( edge[i].e, id );
		}
}

int point[N], tcnt, rot[N], rcnt, oppo[M];
struct tnode{ int e, next; } tree[M];
void Tlink( int s, int e ) {
	tree[++tcnt].e=e; tree[tcnt].next=point[s]; point[s]=tcnt;
}

void Build_tree() {//将原图缩点为一棵树
	for( int i=1; i<=n; i++ ) if( !dfn[i] ) {
		rot[++rcnt]=i; Tarjan( i, -1 );
		Note( i, ++pcnt );
	}
	for( int r=1; r<=n; r++ )
		for( int i=fir[r]; i; i=edge[i].next )
			if( low[r]!=low[ edge[i].e ] )
				Tlink( low[r], low[ edge[i].e ] ), oppo[tcnt]=i;//记录树中的边对应到原图的哪一条边
}


int fa[N], dep[N], siz[N], son[N];
void DFS1( int r, int f, int d ) {  
    dep[r]=d; siz[r]=1;  
    for( int i=point[r]; i; i=tree[i].next )
		if( tree[i].e!=f ) {
			DFS1( tree[i].e, r, d+1 );  
			siz[r]+=siz[ tree[i].e ];  
			if( siz[ tree[i].e ]>siz[ son[r] ] ) son[r]=tree[i].e;
			fa[ tree[i].e ]=r;
		}
}

int top[N], fae[N], sc[N];
void DFS2( int r, int f ) {
	sc[r]=r;
    if( son[r] ) top[ son[r] ]=top[r], DFS2( son[r], r );  
    for( int i=point[r]; i; i=tree[i].next )  
        if( tree[i].e!=f && tree[i].e!=son[r] ) {  
            top[ tree[i].e ]=tree[i].e;  
            DFS2( tree[i].e, r );
        }
		else if( tree[i].e==f ) fae[r]=i;//记录当前点与其父亲点之间的边
}  

int LCA( int p1, int p2 ) {  
    while( top[p1]!=top[p2] ) {  
        if( dep[ top[p1] ]>dep[ top[p2] ] ) p1=fa[ top[p1] ];  
        else p2=fa[ top[p2] ];  
    }  
    return dep[p1]<dep[p2] ? p1 : p2;  
} 

void TCS() {
	for( int i=1; i<=rcnt; i++ ) {
        DFS1( low[ rot[i] ], -1, 1 ),
		top[ low[ rot[i] ] ]=low[ rot[i] ];
		DFS2( low[ rot[i] ], -1 );
	}
}

void Find_way() {
	TCS();
	int q; Getin(q);
	for( int i=1; i<=q; i++ ) {
		Getin(s); Getin(e);
		if( low[s]==low[e] ) continue;
		s=low[s]; e=low[e];
		int lca=LCA( s, e );
		while( s && s!=lca && dep[ sc[s] ]>dep[lca] ) { 
            if( sc[s]!=s ) s=sc[s]; 
            edge[ oppo[ fae[s] ] ].flg=1; 
            sc[s]=lca; s=fa[s]; 
        } 
        while( e && e!=lca && dep[ sc[e] ]>dep[lca] ) { 
            if( sc[e]!=e) e=sc[e]; 
            edge[ oppo[ fae[e] ]^1 ].flg=1; 
            sc[e]=lca; e=fa[e]; 
        } 
	}
}


void Print() {
	for( int i=2; i<=ecnt; i+=2 ) {
		if( edge[i].flg ) printf( "R" );
		else if( edge[i^1].flg ) printf( "L" );
		else printf( "B" );
	}
	putchar(10);
}

int main() {
	Build_map();
	Build_tree();
	Find_way();
	Print();
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值