线段树点区间对应问题

原题链接
参考链接
题意: 给定 n n n个区间,问你可以被你看到的区间个数,按照输入顺序安排区间的前后顺序(输入顺序越后越能被看到)。

题解: 线段树+技巧离散化。这应该是线段树点表区间后的特殊技巧。(刚开始读错题,实际上一个数代表一个长度为1的区间)。我们将给定的区间左右端点离散化后,用线段树暴力。

但是会发现可能某个区间并没有被其他区间完全覆盖但却显示为被覆盖。如区间 [ 1 , 10 ] [ 1 , 4 ] , [ 6 , 10 ] [1,10][1,4],[6,10] [1,10][1,4],[6,10],离散化后 [ 1 , 4 , 6 , 10 ] [1,4,6,10] [1,4,6,10]对应 [ 1 , 2 , 3 , 4 ] [1,2,3,4] [1,2,3,4]。那么对应的离散化被覆盖区间为 [ 1 , 4 ] , [ 1 , 2 ] , [ 3 , 4 ] [1,4],[1,2],[3,4] [1,4],[1,2],[3,4](注意这里 2 2 2 3 3 3对应的是一个长度为 1 1 1的区间,故按照离散化的结果来看这里 [ 1 , 4 ] [1,4] [1,4] [ 1 , 2 ] , [ 3 , 4 ] [1,2],[3,4] [1,2],[3,4]完全覆盖,但是实际却没有。),与实际情况不符,那么可以想到时边界出现了问题。

手模几个例子会发现当两个点实际距离大于1的时候,即两者之间至少有一个块没被两者覆盖,所以我们只需要在两点距离大于2时添加一个点做边界,然后将真实区间插入线段树后进行查询即可。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<vector>

using namespace std;

const int N = 1e5 + 10, M = N << 2;
typedef pair<int, int> PII;
bool vis[M]; PII q[N]; 
int p[M], T, g, tn, n;
int tr[M], ans;

int b_s(int l, int r, int x) {
	while(l < r) {
		int mid = l + r >> 1;
		if(p[mid] >= x) r = mid;
		else l = mid + 1;
	}
	return l;
}

void pushdown(int u) {
	if(tr[u] != -1){ 
		tr[u << 1] = tr[u << 1 | 1] = tr[u];
		tr[u] = -1;
	} 
}

void modify(int u, int ul, int ur, int x, int y, int v) {
	if(ul >= x && ur <= y) {
		tr[u] = v;
		return ;
	}
	
	pushdown(u);
	int mid = ul + ur >> 1;
	if(y <= mid) modify(u << 1, ul, mid, x, y, v);
	else if(x > mid) modify(u << 1 | 1, mid + 1, ur, x, y, v);
	else modify(u << 1, ul, mid, x, mid, v), modify(u << 1 | 1, mid + 1, ur, mid + 1, y, v); 
}

void query(int u, int l, int r) {
	if(tr[u] != -1) {
		if(!vis[tr[u]]) {
			ans++;
			vis[tr[u]] = true;
		}
		return ;
	}
	
	if(l == r) return ;
	int mid = l + r >> 1;
	query(u << 1, l, mid);
	query(u << 1 | 1, mid + 1, r);
}

int main()
{
	scanf("%d", &T);
	while(T--) {
		memset(tr, -1, sizeof tr);
		memset(vis, false, sizeof vis);
		
		
		scanf("%d", &n);
		g = 0; tn = n * 2;
		for(int i = 1; i <= n; i++) {
			int a, b;
			scanf("%d%d", &a, &b);
			q[i] = {a, b};
			p[++g] = a; p[++g] = b;
		}
		
		sort(p + 1, p + 1 + tn);
		g = 0;
		for(int i = 1; i <= tn; ) {
			p[++g] = p[i];
			int j = i + 1;
			while(p[i] == p[j]) j++;
			i = j;
		}
		
		int len = g;
		for(int i = 2; i <= len; i++) 
			if(p[i] - p[i - 1] > 1) p[++g] = p[i - 1] + 1;
		sort(p + 1, p + 1 + g);
		
		for(int i = 1; i <= n; i++) {
			int x = b_s(1, g, q[i].first);
			int y = b_s(1, g, q[i].second);
			modify(1, 1, g, x, y, i);
		}
		
		ans = 0;
		query(1, 1, g);
		
		printf("%d\n", ans);
	}
	
	return 0;
}
全部评论

相关推荐

04-10 11:02
已编辑
北方民族大学 全栈开发
“无名小卒,还是名扬天下?”我知道很多人都不觉得我能走到今天这一步,当然,也包括我自己。在我的人生里,有两部作品刻下了最深的烙印:《斗破苍穹》与《龙族》。它们总被人拿来对照:一边是萧炎的桀骜轻狂,一边是路明非的怯懦衰颓。有人说,天蚕土豆没见过魂天帝,但江南见过真凯撒。我时常觉得,自己就是那个衰小孩路明非。可路明非可以开挂,我不可以;我也无数次幻想过,能拥有萧炎那般年少轻狂的人生,可我没有他与生俱来的逆天天赋。我只是个平庸的普通人,一个看过《斗破苍穹》却开不了挂的路明非,只能一步一步往上爬。从我下定决心找实习的那一刻起,我就给自己定下了目标:“我一定要为字节跳动卖命.jpg”。萧炎有他的三年之约,我有我的两年半之约(其实是一年半)。2024.11.20,科大讯飞的第一封实习offer落进邮箱,我迈出了这场奔赴的第一步。2025.8.18,放弃百度转正的安稳机会,转身走进前路未卜的不确定里。我很感谢我在百度的mentor,是她从茫茫人海选中了我,给了我大厂实习的机会。即便有段时间我状态差、产出不理想,她依旧愿意认可我、希望我留下转正。2025.11.14,我选择走进字节跳动,以实习生的身份重新出发。2026.3.25&nbsp;-&nbsp;3.31,一周速通上海飞书,幸遇赏识我的伯乐,斩获Special&nbsp;Offer。被告知面试通过的那一刻,我的内心无比平静,就像这个offer本就该属于我。不是侥幸,是应得的。这一路,有人看轻过我的出身,不相信我能走到这里;也有人在我看不见前路的时候,替我举过灯。没有他们的鼓励与支撑,就没有今天站在这里的我。我看到了自强不息的激荡,那是一个双非的伟大乐章!我是雨夜迈巴赫,我要开启属于我的新篇章了。
在看牛客的本杰明很勇...:真心祝贺l总 我永远的偶像 我滴神
春招至今,你收到几个面试...
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务