线段树点区间对应问题

原题链接
参考链接
题意: 给定 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值