【CodeForces - 1080F】Katya and Segments Sets


@Desciption@

It is a very important day for Katya. She has a test in a programming class. As always, she was given an interesting problem that she solved very fast. Can you solve that problem?

You are given n ordered segments sets. Each segment can be represented as a pair of two integers [l,r] where l≤r. Each set can contain an arbitrary number of segments (even 0). It is possible that some segments are equal.

You are also given m queries, each of them can be represented as four numbers: a,b,x,y. For each segment, find out whether it is true that each set p (a≤p≤b) contains at least one segment [l,r] that lies entirely on the segment [x,y], that is x≤l≤r≤y.

Find out the answer to each query.

Note that you need to solve this problem online. That is, you will get a new query only after you print the answer for the previous query.

Input
The first line contains three integers n, m, and k (1≤n,m≤105,1≤k≤3⋅105) — the number of sets, queries, and segments respectively.

Each of the next k lines contains three integers l, r, and p (1≤l≤r≤109,1≤p≤n) — the limits of the segment and the index of a set, to which this segment belongs.

Each of the next m lines contains four integers a,b,x,y (1≤a≤b≤n,1≤x≤y≤109) — the description of the query.

Output
For each query, print “yes” or “no” in a new line.

Example
input
5 5 9
3 6 3
1 3 1
2 4 2
1 2 3
4 6 5
2 5 3
7 9 4
2 3 1
4 10 4
1 2 2 3
1 2 2 4
1 3 1 5
2 3 3 6
2 4 2 9
output
no
yes
yes
no
yes

Note
For the first query, the answer is negative since the second set does not contain a segment that lies on the segment [2,3].

In the second query, the first set contains [2,3], and the second set contains [2,4].

In the third query, the first set contains [2,3], the second set contains [2,4], and the third set contains [2,5].

In the fourth query, the second set does not contain a segment that lies on the segment [3,6].

In the fifth query, the second set contains [2,4], the third set contains [2,5], and the fourth contains [7,9].

@Translation@

给定 k 条线段,每一条线段属于 1~n 中的某一个集合。
m 次询问,每次询问形如 (a, b, x, y),即询问是否集合 a~b 中,都存在一条线段被线段[x, y]覆盖。
k <= 3*105;n, m<=105

@Solution@

对于这种题当然是先考虑暴力,再考虑用数据结构优化。

有两种思维方向,一种是从线段为元素出发,查询[x, y]内的线段是否能将[a, b]这些集合囊括入内;一种是从集合为元素出发,查询[a, b]这个区间的集合是否都有线段在[x, y]之间。

第二个一定会比第一个好做,不然题目给你[a, b]这个区间是白给的吗-_-。

考虑对于某一个集合,它包含某一条线段[l, r]在[x, y]之间,相当于满足 r <= y 的线段中最大的 l >= x。

相当于我们在满足 r <= y 的前提下,求出[a, b]集合中的线段最大的 l,将这些 l 取最小值 lmin。如果 lmin >= x,则有解,输出 yes;否则输出 no。

在满足 r <= y 的前提下,我们可以用可持久化线段树来搞。按照 r 从小到大的顺序,将 l 依次加入线段树并维护历史版本。这样就可以取得任意的 r <= y 情况下的线段树。就可以进行相应的询问。

@Code@

这一场 CF 的题目质量还算不错吧。有考算法,考思维推结论,还有考(简单的)数据结构。
不过当时看都没来得及看这道题……

#include<cstdio>
#include<algorithm>
using namespace std;
const int MAXN = 100000;
const int MAXK = 300000;
const int INF = (1<<30);
struct Segment{
	int l, r, p;
	Segment(int _l=0, int _r=0, int _p=0):l(_l), r(_r), p(_p){}
}seg[MAXK + 5];
bool operator < (Segment a, Segment b) {
	return a.r < b.r;
}
struct SegmentTree{
	SegmentTree *ch[2]; int mn;
}tree[MAXK*20 + 5], *root[MAXK + 5], *NIL, *tcnt;
void init() {
	NIL = tcnt = &tree[0];
	NIL->ch[0] = NIL->ch[1] = NIL;
	NIL->mn = -INF;
}
void insert(SegmentTree *rt1, SegmentTree *&rt2, int pos, int key, int le, int ri) {
	rt2 = (++tcnt); *(rt2) = *(rt1);
	if( le == ri ) rt2->mn = max(rt2->mn, key);
	else {
		int mid = (le + ri) >> 1;
		if( pos <= mid )
			insert(rt1->ch[0], rt2->ch[0], pos, key, le, mid);
		else insert(rt1->ch[1], rt2->ch[1], pos, key, mid+1, ri);
		rt2->mn = min(rt2->ch[0]->mn, rt2->ch[1]->mn);
	}
}
int query(SegmentTree *rt, int le, int ri, int ql, int qr) {
	if( ql > ri || qr < le )
		return INF;
	else if( ql <= le && ri <= qr )
		return rt->mn;
	int mid = (le + ri) >> 1;
	return min(query(rt->ch[0], le, mid, ql, qr), query(rt->ch[1], mid+1, ri, ql, qr));
}
int main() {
	init(); int n, m, k;
	scanf("%d%d%d", &n, &m, &k);
	for(int i=1;i<=k;i++)
		scanf("%d%d%d", &seg[i].l, &seg[i].r, &seg[i].p);
	sort(seg+1, seg+k+1); root[0] = NIL;
	for(int i=1;i<=k;i++)
		insert(root[i-1], root[i], seg[i].p, seg[i].l, 1, n);
	for(int i=1;i<=m;i++) {
		int a, b, x, y;
		scanf("%d%d%d%d", &a, &b, &x, &y);
		int p = upper_bound(seg+1, seg+k+1, Segment(0, y, 0)) - seg - 1;
		int q = query(root[p], 1, n, a, b);
		if( q >= x ) puts(q >= x ? "yes" : "no");
		fflush(stdout);
	}
}

@End@

就是这样,新的一天里,也请多多关照哦(ノω<。)ノ))☆.。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值