区间操作
区间操作
区间交集
1、用途
若干区间求交集
2、原理
定义区间\(I:\{x|a\le x\le b\}\),以下简记为\([a,b]\)。
注意到两个区间交集的左端点一定是其中某一个区间的左端点,右端点也一定是其中某一个区间的右端点。于是区间\(I_1:[l_1,r_1]\)与\(I_2:[l_2,r_2]\)的交集为\([max(l_1,l_2),min(r_1,r_2)]\)。
由交集的表达式推出\(I_1\)与\(I_2\)有交集的充要条件为\(\begin{cases}l_1\le r_2\\ l_2\le r_1 \end{cases}\)。
3、复杂度
\(O(n)\)
4、模板
struct seg
{
int l, r;
bool operator <(seg others)
{
if (l == others.l) return r < others.r;
else return l < others.l;
}
};
seg intersec(vector <seg> &a) //若无交集,返回值l > r
{
int l = -INF, r = INF;
for (int i = 0; i < n; ++i)
{
l = max(l, a[i].l), r = min(r, a[i].r);
if (l > r) return {l, r};
//有交集时的逻辑
}
return {l, r};
}
5、备注
①求交集时不需要排序。
区间合并
1、用途
若干区间求并集
2、原理
定义区间\(I:\{x|a\le x\le b\}\),以下简记为\([a,b]\)。
首先按区间左端点对所有区间进行排序。
假设之前已经完成合并操作的区间左端点为\(l\),右端点为\(r\),目前正在合并第\(i\)个区间。
由于左端点的有序性,因此\(l \le l[i]\)。下面分两种情况讨论。
①:\(l[i]\le r\)。此时第\(i\)个区间与之前合并完成的区间有交集,意味着之前合并完成的区间有向右拓展的可能。这时只需要\(r=max(r,r[i])\)即可。
②:\(l[i]>r\)。此时第\(i\)个区间与之前合并完成的区间没有交集。而由于区间左端点的有序性,在此之后的所有区间\(I_j\)均满足\(l[j] \ge l[i] >r\),因此从\(i\)开始往后的所有区间都和当前已合并完成的区间没有交集。这时将\(\{l,r\}\)加入答案,并把\(l\)重新赋值成\(l[i]\)、\(r\)重新赋值成\(r[i]\)、继续向后遍历即可。
3、复杂度
\(O(nlogn)\)
4、模板
struct seg
{
int l, r;
bool operator <(seg others)
{
if (l == others.l) return r < others.r;
else return l < others.l;
}
};
vector <seg> unite(vector <seg> &a)
{
vector <seg> ans;
if (a.empty()) return ans; //小心re
sort(a.begin(), a.end());
int l = a[0].l, r = a[0].r;
for (int i = 1; i < a.size(); ++i)
if (a[i].l <= r) r = max(r, a[i].r);
else ans.push_back({l, r}), l = a[i].l, r = a[i].r;
ans.push_back({l, r});
return ans;
}
5、备注
①求并集时需要排序。