【题解】[CCO2020] Interval Collection
支持向集合中加入或删除一格区间,并支持询问,求出一个子集,在满足最大公共子区间最小的前提下,最小公共超区间的最小值。最小公共超区间指包含集合中所有区间的最小的区间。
首先选取的集合只包含两个区间。
分开讨论,如果所有区间的最大公共子区间不为空,那么一定有一个区间卡在右端点,另一个卡在左端点,只用选这两个即可。
否则一定存在两个区间没有交集,多选一个只会使答案更劣。
这提示我们对于答案也可以这样分开讨论。
如果所有区间的最大公共子区间不为空,我们令这个子区间为 \([l,r]\) 。那么我们选取的两个区间一定是左端点为 \(l\) 中右端点最小的,和右端点为 \(r\) 中左端点最大的。
否则一定存在一个位置 \(p\), \([1,p]\) 中有完整区间,\([p,10^6]\) 中有完整区间。
那么我们可以枚举这个 \(p\) ,然后查询左边左端点的最大值,和右边左端点的最小值,单次 \(\mathcal{O}(N)\) 。
但是这种方法不好扩展,我们考虑枚举 \(p\le q\) 的二元组 \((p,q)\) ,然后查询右端点为 \(p\) 的左端点最大值,和左端点为 \(q\) 的右端点最小值。
可以直接 \(\mathcal{O}(N^2)\) 枚举,但这种类似二维数点的问题可以直接分治做到 \(\mathcal{O}(N\log N)\) 。
而我们知道线段树的结构非常类似分治树的结构,所以我们用线段树模拟分治树,一次插入/删除相当于两个单点修改,查询就是线段树根节点的答案。
时间复杂度 \(\mathcal{O}(N+Q\log N)\) 。
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define pre(i,a,b) for(int i=a;i>=b;i--)
#define N 1000005
#define inf 0x3f3f3f3f
using namespace std;
multiset<int>p,q,u[N],v[N];
struct node{int l,r,mxl,mnr,sum;}a[N<<2];
#define L a[x].l
#define R a[x].r
#define ls (x<<1)
#define rs (ls|1)
void build(int x,int l,int r){
L=l;R=r;a[x].mxl=-inf,a[x].mnr=a[x].sum=inf;
if(l==r)return;
int mid=(l+r)>>1;
build(ls,l,mid);
build(rs,mid+1,r);
}
void modify(int x,int pos){
if(L==R){
if(u[L].size())a[x].mnr=*u[L].begin();else a[x].mnr=inf;
if(v[L].size())a[x].mxl=*v[L].rbegin();else a[x].mxl=-inf;
a[x].sum=a[x].mnr-a[x].mxl;
//cout<<"ss "<<x<<" "<<L<<" "<<R<<" "<<pos<<" "<<a[x].sum<<" "<<a[x].mxl<<" "<<a[x].mnr<<endl;
return;
}
int mid=(L+R)>>1;
if(mid>=pos)modify(ls,pos);
else modify(rs,pos);
a[x].mxl=max(a[ls].mxl,a[rs].mxl);
a[x].mnr=min(a[ls].mnr,a[rs].mnr);
a[x].sum=min(min(a[ls].sum,a[rs].sum),a[rs].mnr-a[ls].mxl);
//cout<<"ss "<<x<<" "<<L<<" "<<R<<" "<<pos<<" "<<a[x].sum<<" "<<a[x].mxl<<" "<<a[x].mnr<<endl;
}
int main(){
build(1,1,N-5);int T;scanf("%d",&T);
char op[2];int l,r;
while(T--){
scanf("%s%d%d",op,&l,&r);
if(*op=='A'){
p.insert(l);q.insert(r);
u[l].insert(r);v[r].insert(l);
modify(1,l);modify(1,r);
}
else{
p.erase(p.find(l));q.erase(q.find(r));
u[l].erase(u[l].find(r));v[r].erase(v[r].find(l));
modify(1,l);modify(1,r);
}
if(*p.rbegin()<*q.begin()){
int l=*p.rbegin(),r=*q.begin();
printf("%d\n",*u[l].begin()-*v[r].rbegin());
}
else printf("%d\n",a[1].sum);
}
return 0;
}