P9166 [省选联考 2023] 火车站

P9166 [省选联考 2023] 火车站

这道题很抽象,有这么几点注意事项

1,火车必须走到尽头才可以停下,所以答案一定会出于输入的这些端点

2,火车只能往一个方向走,不可以在中途换向

那么这题怎么处理?不会真的要一波操作然后把所有答案排个序吧?

我选择标记法!标记答案,省去了排序的过程。

那么哪些端点是答案?

这是个大问题!

那么我们开始构思,头脑风暴法?瞪眼法!考场三个小时直接不断优化,不断更新思路

从一开始的想要建图,想要掏出杀手锏search,到后来冷静分析,用有限的数据找到题目的精髓,找到答案统计的本质,经过一波分析,我决定从新出发,用最简朴的方式,最朴素的算法切掉这题,数据规模不大,才2e5,那么我们可以直接相信奥义,既然题干告诉你这是火车站,那么我们化身为工程师了,总览全局,真正融入修铁路这门事业

考虑到可能数据规模较大,我们就要边输入边扩建,从起点扩建范围,只有与已建好铁路范围有交集的铁路才可以实现已建好铁路的扩建,那么输入的过程顺便修点铁路也可以神奇的省去复杂度,早开工早下班

那么假设我们已经拿到了一段修好的铁路,什么是答案?

分为以下几种情况

1,这条轨道区间在已经建好的范围内

x在这条轨道区间内 左右端点都是答案

x在这条轨道区间左边 右端点是答案

x在这条轨道区间右边 左端点是答案

2,这条轨道左端点在起点和已经建好的右端点之间,右端点在外边

右端点是答案

3,这条轨道右端点在已经建好的左端点和起点之间,左端点在外边

左端点是答案

4,已经建好的在这条轨道区间的范围内

这一轮没用,先丢进队列

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <iostream>
#include <queue>
using namespace std;
struct pp {
	int a, b;
} e[200001];
queue<int> q;
int now_l = 200001, now_r, cnt, w, copy_m/*剩余轨道数量*/;
bool ans[200001], p[200001];
int main() {
	int n, m, x;
	scanf("%d%d%d", &n, &m, &x);
	copy_m = m;
	for(int i = 1, l, r; i <= m; ++i) {
		scanf("%d%d", &l, &r);//输入轨道边界
		e[++cnt].a = l;//存轨道
		e[cnt].b = r;
		q.push(cnt);//以轨道编号压入队列
		if(l <= x && r >= x) now_l = min(l, now_l), now_r = max(r, now_r);//扩建
	}
	int now;
	while(!q.empty()) {
		now = q.front();
		q.pop();
		if(e[now].a >= now_l && e[now].b <= now_r) {//这条轨道在建好的轨道之间
			if(e[now].a <= x && e[now].b >= x)//起点在这条轨道上
				ans[e[now].a] = 1, ans[e[now].b] = 1;//这条轨道两端都可以到
			else if(e[now].a >= x && e[now].b >= x) {//整个轨道在起点右边
				ans[e[now].b] = 1;//这条轨道只有右端点可以到
			} else if(e[now].a <= x && e[now].b <= x) {//整个轨道在起点左边
				ans[e[now].a] = 1;//这条轨道只有左端点可以到
			}
			copy_m--;
			w = 0;
			continue;
		} else if(e[now].a >= x && e[now].a <= now_r && e[now].b > now_r) {//这条轨道左端点在已经建好的轨道范围内,右端点出界
			now_r = e[now].b;//扩建
			ans[e[now].b] = 1;//右端点可以到
			copy_m--;
			w = 0;
			continue;
		} else if(e[now].a < now_l && e[now].b >= now_l && e[now].b <= x) {//这条轨道右端点在已经建好的轨道范围内,左端点出界
			now_l = e[now].a;//扩建
			ans[e[now].a] = 1;//左端点可以到
			copy_m--;
			w = 0;
			continue;
		} else {//与已经建好的轨道无交集
			q.push(now);//再次压入队列
			w++;
			if(w == copy_m) break;//如果连续压入队列的次数等于剩余轨道数量,说明剩下的轨道是飞舞,退出循环
		}
	}
	for(int i = 1; i <= n; i++) {
		if(i == x) continue;
		if(ans[i]) printf("%d ", i);//被标记过的点可以到达
	}
	return 0;
}

上面的思路貌似无懈可击,这还不够,我们依旧不满足

一个叫做(名字)的人曾说过,人们最初的想法是冗杂的。

嗯对,他确实说过

其实上面的思路已经很明确了,我们浅浅的总结一下,可以省去不少麻烦

1,这条轨道左端点在起点和修好的铁路右端点之间,说明可以换道,

右端点是答案,新的右端点保留最大值

2,这条轨道右端点在修好的铁路左端点和起点之间,说明可以换道,

左端点是答案,新的左端点保留最小值

3,这条轨道左端点在起点左边,右端点在x右边

左右端点都是答案,新的右端点保留最大值,同时新的左端点保留最小值

4,已经建好的在这条轨道区间的范围内

这一轮没用,先丢进队列

不错,起码码量分情况简便了许多

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <iostream>
#include <queue>
using namespace std;
struct pp {
	int a, b;
} e[200001];
queue<int> q;
int now_l = 200001, now_r, cnt, w, copy_m/*剩余轨道数量*/;
bool ans[200001], p[200001];
int main() {
	int n, m, x;
	scanf("%d%d%d", &n, &m, &x);
	copy_m = m;
	for(int i = 1, l, r; i <= m; ++i) {
		scanf("%d%d", &l, &r);//输入轨道边界
		e[++cnt].a = l;//存轨道
		e[cnt].b = r;
		q.push(cnt);//以轨道编号压入队列
		if(l <= x && r >= x) now_l = min(l, now_l), now_r = max(r, now_r);//扩建
	}
	int now;
	while(!q.empty()) {
		now = q.front();
		q.pop();
		if(e[now].a >= x && e[now].a <= now_r) {
			ans[e[now].b] = 1;
            now_r = max(now_r , e[now].b);
			copy_m --;
			w = 0;
			continue;
		} else if(e[now].b <= x && e[now].b >= now_l) {
			ans[e[now].a] = 1;
            now_l = min(now_l , e[now].a);
			copy_m --;
			w = 0;
			continue;
		} else if(e[now].a <= x && e[now].b >= x) {
			now_l = min(now_l , e[now].a);
            now_r = max(now_r , e[now].b);
			ans[e[now].a] = 1;
            ans[e[now].b] = 1;
			copy_m --;
			w = 0;
			continue;
		} else {
			q.push(now);
			w++;
			if(w == copy_m) break;
		}
	}
	for(int i = 1; i <= n; i++) {
		if(i == x) continue;
		if(ans[i]) printf("%d ", i);//被标记过的点可以到达
	}
	return 0;
}

posted @ 2023-05-07 17:32  朝绾曦梦  阅读(68)  评论(0编辑  收藏  举报