[CCO 2020] Interval Collection 解题报告
P6645 [CCO 2020] Interval Collection 题解
题意概述
给定一个由区间构成的可重集 \(S\),求 \(S\) 的一个非空子集,使得 \(P\),使得在所有 \(S\) 的非空子集中,\(P\) 中所有区间的交最小,且 \(P\) 中所有区间的右端点最大值和左端点最小值的差最小。多次询问,\(S\) 会加入和删除区间,保证所有操作合法。
初步分析
自己计算求几次符合条件的非空子集可以发现,一定存在一种构造方案,使得子集中只由两个区间构成。
这是很容易证明的:假设子集中原来只有 \([l_1,r_1]\) 和 \([l_2,r_2]\) 两个区间(\(r_1 \le l_2\)),那么它们的交为 \([l_2,r_1]\);如果加入第三个区间 \([l_3,r_3]\) 使得它们的交改变了,那么新的交要么是 \([l_2,r_3]\),要么要是 \([l_3,r_1]\),要么是 \([l_3,r_3]\)。可以发现新的交只由三个区间中的一个或两个区间的端点构成,因此完全可以只用这一个或两个区间构成交更小的子集。
那么问题转化为怎么找到这两个符合条件的区间。这个问题比较难,我们可以简化问题:不带修怎么做。
为了便于思考,我们可以将区间分成两类。一类是交为空的;另一类是交不为空的。
对于第一类,我们可以考虑先将所有区间按左端点从小到大排序。对于一个区间 \([l,r]\),与其交最小要么是最后一个区间,要么是左端点大于 \(r\) 的且右端点最小的区间。枚举即可。
对于第二类,我们发现,如果作为答案的两个区间交非空,那么说明此时所有区间的交非空,并且等于作为答案的两个区间交。所以,我们只需要求出所有区间的交 \([l,r]\),并且找到以 \(l\) 为左端点的区间的右端点最小值和以 \(r\) 为右端点的区间的左端点最大值。
为了快速得到这两个值,我们想到:对于每一个位置,维护以这个位置为左端点的区间的右端点最小值,和以这个位置为右端点的区间的左端点最小值。
深入分析
进一步地,我们发现上面所述的信息具有可合并性,因此可以用线段树维护。具体地,线段树上一个维护区间 \([l,r]\) 需要维护左端点在 \([l,r]\) 的区间的右端点最小值 \(mxr\) 和右端点在 \([l,r]\)为的区间的左端点最小值 \(mnl\)。
但是这还不够,为了求出由没有交的区间构成的答案 \(ans\),我们还需要再维护当前节点的答案。为了确保这个构成这个答案的区间的交一定非空,非叶子节点的答案为左儿子的 \(mxr\) 减去右儿子的 \(mxr\);叶子节点就用自己的 \(mxr\) 和 \(mxr\) 相减就行。最后答案就是根节点维护的答案。
如果构成这个答案的区间由交非空的区间构成。我们按之前所述查询即可。
让我们动起来
解决了静态问题,现在该思考怎么动态维护。我们可以这么思考:现在我们已经处理完了前若干的区间,现在新加入/删除了一个区间 \([l,r]\),我们应该怎么维护?
这很简单,在 \(l\) 位置更新右端点最小值,在 \(r\) 位置更新最大值。但是我们发现,在加入区间时,即使不能作为最值的数据,它也不能丢弃,因为在最值对应区间被删除后,它可能成为新的最值。
因此我们需要一种数据结构,它可以有序地存储数据,并且支持修改,时间复杂度为 \(O(logn)\) 及以下。这种数据结构可以是优先队列。
但是有个问题:在删除时,非堆顶元素无法直接删除。考虑到:一个原本不在堆顶的元素,对应区间被删除后,还会因为操作来到堆顶,当且仅当这个操作为删除操作。所以我们可以考虑记录每个区间的个数,在删除操作时,如果新的堆顶元素对应区间以及被删除了,那么将其弹出,再反复这个过程。
这么做时间复杂度对不对呢?我们可以分析:
- 对于加入区间,每一次线段树单点修改并往优先队列压入元素都是\(O(log^2Q)\)的,因此这部分的时间复杂度为 \(O(Qlog^2Q)\);
- 对于删除区间,因为每个区间的每个端点最多进出堆各一次,再乘上线段树单点修改的开销,这部分的时间复杂度为 \(O(Qlog^2Q)\)。
因此总时间复杂度为 \(O(Qlog^2Q)\)(实际跑下来最慢点也不到 \(1.2s\))。
talk is cheap,show me the code
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define For(i,s,t) for(int i=s;i<=t;i++)
#define Down(i,s,t) for(int i=s;i>=t;i--)
#define ls (i<<1)
#define rs (i<<1|1)
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
inline int min(int x,int y){return x<y?x:y;}
inline int max(int x,int y){return x>y?x:y;}
inline int read(){
register int x=0;
char c=getchar();
while(c<'0' || '9'<c) c=getchar();
while('0'<=c && c<='9') x=(x<<1)+(x<<3)+c-'0',c=getchar();
return x;
}
void write(int x){
if(x>=10) write(x/10);
putchar(x%10+'0');
}
const int N=1e6+10;
int n;
struct Tree{int l,r,mnr,mxl,ans;}t[N<<2];
void push_up(int i){
t[i].mnr=min(t[ls].mnr,t[rs].mnr);
t[i].mxl=max(t[ls].mxl,t[rs].mxl);
t[i].ans=min(t[ls].ans,t[rs].ans);
t[i].ans=min(t[i].ans,t[rs].mnr-t[ls].mxl);
}
priority_queue<int,vector<int>,less<int> > q1[N];//大根堆
priority_queue<int,vector<int>,greater<int> > q2[N];//小根堆
map<pii,int> exist;
void build(int i,int l,int r){
t[i].l=l,t[i].r=r;
if(l==r){
t[i].mnr=inf;
t[i].mxl=-inf;
t[i].ans=inf;
return;
}
int mid=l+r>>1;
build(ls,l,mid);
build(rs,mid+1,r);
push_up(i);
}
void update(int i,int x,int type,int val){
if(t[i].l==t[i].r){
if(type)
q1[x].push(val),t[i].mxl=q1[x].top();
else
q2[x].push(val),t[i].mnr=q2[x].top();
t[i].ans=min(t[i].ans,t[i].mnr-t[i].mxl);
return;
}
int mid=t[i].l+t[i].r>>1;
if(x<=mid)
update(ls,x,type,val);
else
update(rs,x,type,val);
push_up(i);
}
void Del(int i,int x,int type){
if(t[i].l==t[i].r){
if(type){
while(!q1[x].empty() && exist[make_pair(q1[x].top(),x)]==0) q1[x].pop();
t[i].mxl=(!q1[x].empty() ? q1[x].top() : -inf);
}
else{
while(!q2[x].empty() && exist[make_pair(x,q2[x].top())]==0) q2[x].pop();
t[i].mnr=(!q2[x].empty() ? q2[x].top() : inf);
}
t[i].ans=t[i].mnr-t[i].mxl;
return;
}
int mid=t[i].l+t[i].r>>1;
if(x<=mid)
Del(ls,x,type);
else
Del(rs,x,type);
push_up(i);
}
int query(int i,int x,int type){
if(t[i].l==t[i].r)
return type?t[i].mxl:t[i].mnr;
int mid=t[i].l+t[i].r>>1;
return x<=mid?query(ls,x,type):query(rs,x,type);
}
char opt[5];
int main()
{
n=read();
int l,r;
build(1,1,1e6);
For(i,1,n){
scanf("%s",opt),l=read(),r=read();
if(opt[0]=='A'){
exist[make_pair(l,r)]++;
update(1,l,0,r);
update(1,r,1,l);
}
else{
exist[make_pair(l,r)]--;
Del(1,l,0);
Del(1,r,1);
}
if(t[1].mxl>t[1].mnr)
write(t[1].ans);
else
write(query(1,t[1].mxl,0)-query(1,t[1].mnr,1));
putchar('\n');
}
return 0;
}

浙公网安备 33010602011771号