CF1080F 题解

CF1080F 题解

$\text{Description}$

给定 $n$ 个集合,每个集合里有若干条线段,共有 $k$ 条,$m$ 次询问,每次给定 $a,b,x,y$,询问编号在 $[a,b]$ 内的集合是否都有一条线段在 $[x,y]$ 内。

$1\le n,m\le 10^5,1\le k\le 3\times 10^5$

$\text{Solution}$

首先分析,若有某个集合内有一条线段在 $[x,y]$ 内,则要求其中所有满足 $l\in[x,y]$ 的线段中,$r$ 的最小值 $\in[x,y]$。实际上,由于 $r\ge l$ 一定成立,所以为了方便(询问一个前缀通常比询问一个区间来的简单),我们将 $l\in[x,y]$ 这一条件改成 $l\in [x,+\infty]$,这里的 $+\infty$ 在实现中可以看作是最大的 $l$。

因此,对于两条线段 $[l_x,r_x],[l_y,r_y]$,若 $l_x=l_y$,则我们只需考虑 $r$ 小的那一条线段。

回到问题本身,这种多维度限制的题目一般可以用排序来去除某一个维度的影响,注意到我们将询问中对 $l$ 的限制变成了一个后缀/前缀,我们将所有线段按 $l$ 降序排列,则询问中对 $l$ 的限制是一个前缀。

既然是询问一个前缀,这启发我们使用主席树。对于某一个前缀来说,其意义是所有满足 $l$ 大于等于某一个值的线段的集合。由于询问中对集合编号的限制是一个区间,我们在建树时不妨以集合编号为下标,那么此时某一个叶子节点存的就应当是其对应的集合中最小的 $r$。由于询问中要求区间内的所有集合都要满足条件,故该主席树中任意一个非叶子节点的权值是其儿子权值的最大值。

那么每次询问时,先二分找到需要的前缀(大于等于 $x$ 的最小的 $l$),然后在其上询问 $[a,b]$ 的最大值,若小于等于 $y$,则返回 $\texttt {yes}$,否则返回 $\texttt{no}$。

$\text{Code}$
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
#include<bits/stdc++.h>
#define REG register
using namespace std;
inline void read(int& x){
REG char c;
while(!isdigit(c=getchar()));x=c^48;
while(isdigit(c=getchar()))x=(x*10)+(c^48);
}

const int N=100005,K=300005,INF=0x7f7f7f7f;

int n,m,k;

struct Seg{int l,r,num;}seg[K];
inline bool cmp(Seg x,Seg y){return x.l>y.l;}
int srt[K];
inline bool cmp1(int x,int y){return x>y;}

int Rt[K],tot;
struct Tree{int ls,rs,val;}t[N*100];

void Build(int& now,int l,int r){
t[now=++tot].val=INF;
if(l==r) return;
REG int mid=(l+r)>>1;
Build(t[now].ls,l,mid),Build(t[now].rs,mid+1,r);
}

void Insert(int pre,int& now,int l,int r,int pos,int S){
t[now=++tot]=t[pre];
if(l==r) return (void)(t[now].val=min(t[now].val,S));
REG int mid=(l+r)>>1;
if(mid>=pos) Insert(t[pre].ls,t[now].ls,l,mid,pos,S);
else Insert(t[pre].rs,t[now].rs,mid+1,r,pos,S);
t[now].val=max(t[t[now].ls].val,t[t[now].rs].val);
}

int Ask(int now,int l,int r,int x,int y){
if(!now) return INF;
if(l>=x&&r<=y) return t[now].val;
REG int Ans=0,mid=(l+r)>>1;
if(mid>=x) Ans=max(Ans,Ask(t[now].ls,l,mid,x,y));
if(mid<y) Ans=max(Ans,Ask(t[now].rs,mid+1,r,x,y));
return Ans;
}

inline void Work(){
read(n),read(m),read(k);
for(REG int i=1;i<=k;++i) read(seg[i].l),read(seg[i].r),read(seg[i].num),srt[i]=seg[i].l;
sort(seg+1,seg+k+1,cmp),sort(srt+1,srt+k+1,cmp1);
Build(Rt[0],1,n);
for(REG int i=1;i<=k;++i) Insert(Rt[i-1],Rt[i],1,n,seg[i].num,seg[i].r);
while(m--){
int a,b,x,y;
read(a),read(b),read(x),read(y);
int l=1,r=k,mid,ans=0;
while(l<=r){
mid=(l+r)>>1;
if(srt[mid]>=x) ans=mid,l=mid+1;
else r=mid-1;
}
int M=Ask(Rt[ans],1,n,a,b);
if(M<=y) puts("yes");
else puts("no");
fflush(stdout);
}
}

int main(){Work();}
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×