【ybtoj高效进阶 21186】道路航线(二分)(bfs)

道路航线

题目链接:ybtoj高效进阶 21186

题目大意

有 n 个点,然后会不断加入一些单向边或者双向边。
然后问你分别在加入第几条边之后,从 a 可以到 b,和从 b 可以到 a。

思路

其实有一个比较经典的做法就是二分加 bfs 判是否可以走到。

不过还有一个 O ( n + m ) O(n+m) O(n+m) 的算法就是记搜。
大概就是如果一条边从能到的点连向了不能到的点,那这个点就能到了,我们就可以从新到的点开始 bfs 那些不能到的点,由于所有点会从不到变成到,所以这里是 O ( n ) O(n) O(n),枚举边是 O ( m ) O(m) O(m),所以是 O ( n + m ) O(n+m) O(n+m)

这里就不写代码了。

代码

#include<queue>
#include<cstdio>
#include<cstring>

using namespace std;

struct node {
    
    
	int to, nxt;
}e[6000005];
int n, m, a, b, le[3000005], KK;
int u[3000005], v[3000005], op[3000005];
bool in[3000005];
queue <int> q;

void add(int x, int y) {
    
    
	e[++KK] = (node){
    
    y, le[x]}; le[x] = KK;
}

bool ck(int m, int s, int t) {
    
    
	KK = 0; memset(le, 0, sizeof(le));
	memset(in, 0, sizeof(in));
	while (!q.empty()) q.pop();
	
	for (int i = 1; i <= m; i++) {
    
    
		add(u[i], v[i]);
		if (!op[i]) add(v[i], u[i]);
	}
	
	in[s] = 1; q.push(s);
	while (!q.empty()) {
    
    
		int now = q.front();
		q.pop();
		
		for (int i = le[now]; i; i = e[i].nxt)
			if (!in[e[i].to]) {
    
    
				in[e[i].to] = 1;
				if (e[i].to == t) return 1;
				q.push(e[i].to);
			}
	}
	
	return 0;
}

int work(int s, int t) {
    
    
	int l = 1, r = m, re = 0;
	while (l <= r) {
    
    
		int mid = (l + r) >> 1;
		if (ck(mid, s, t)) re = mid, r = mid - 1;
			else l = mid + 1;
	}
	return re;
}

int main() {
    
    
//	freopen("road.in", "r", stdin);
//	freopen("road.out", "w", stdout);
	
	scanf("%d %d %d %d", &n, &m, &a, &b);
	for (int i = 1; i <= m; i++) {
    
    
		scanf("%d %d %d", &u[i], &v[i], &op[i]);
	}
	
	printf("%d %d", work(a, b), work(b, a));
	
	return 0;
}

Guess you like

Origin blog.csdn.net/weixin_43346722/article/details/121344286