CF1764H 题解

题意

给定长为 \(n\) 的序列,初始 \(a_i=i\)。有 \(m\) 个操作,操作为给出 \(l_i,r_i\) 并将 \([a_{l_i},a_{r_i}]\) 赋值为 \(a_{l_i}\)。要求对每个 \([i,i+k-1]\),回答保留该区间内的操作后序列中不同元素的数量。

初步分析

不难发现,对答案产生贡献的有两种情况:

  • 没有被执行操作的元素

  • 某次操作左端点的元素

我们尝试分别统计这两种情况。

只考虑维护序列,我们容易想到一种基于均摊的,用 set 暴力维护的做法:在 set 中存下每一个区间,遇到修改操作就找到左右端点所在的 set 区间并将其分裂,暴力删去左右端点之间的区间,插入新区间。

我们想在上述过程中同时预处理我们需要的东西。

进一步分析

对于第一种情况:我们对 set 中的每个区间同时记录下一个 \(time\),表示这个区间最初是第 \(time\) 次修改操作插入进去的。假设当前是第 \(i\) 次修改操作,将从 set 中删去 \([l,r]\) 这个区间,那么我们可以知道在 \([time+1,i-1]\) 这个时间段以内 \([l,r]\) 是没有被修改过的,可以作为第一种贡献。贡献的范围是所有 \(ql>time\and qr<i\) 的询问操作。这是一个经典的二维数点问题,可以用扫描线+树状数组解决。

对于第二种情况:我们需要对每个操作维护 \(3\) 个信息,分别是:\(pre_i\),该操作之前最后一个覆盖到 \(l_i\) 的操作;\(go_i\),该操作的原始区间 \([l_i,r_i]\) 被完全覆盖掉的时刻;\(ed_i\),该操作彻底不能产生贡献的时刻。如果维护好了这些信息,我们知道这个操作能对 \(pre_i<ql\and qr<ed_i\and ql\le i\le qr\) 的询问产生贡献,这同样是一个二维数点问题。

具体的,\(pre_i\) 只需要在 set 中插入该操作之前查询即可;\(go_i\) 为所有 \(time=i\) 的区间中被删去时刻的最大值;\(ed_i=\max (go_i,ed_j[pre_j==i])\)

这样这道题就完全解决了。

代码实现

感觉只有必要放一下预处理,都来做这题了不至于不会二维数点吧:

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
struct bignod{
int l,r,tim;
bool operator<(bignod b)const{
return l<b.l;
}
};
struct gonxian{
int x,y,v;
}gx[2000015];
void init(){
set<bignod> st;
set<bignod> ::iterator it,ti,it2;
st.insert(bignod{1,n,0});
for(int i=1;i<=m;i++){
int l=xg[i].l,r=xg[i].r;
it=st.upper_bound(bignod{l,0,0}),ti=--st.upper_bound(bignod{r,0,0});
it2=it,it2--;
bignod u=*it2,v=u;
pre[i]=u.tim;
if(u.r>=xg[i].r){
go[u.tim]=max(go[u.tim],i);
st.erase(it2);
if(u.l<l)st.insert(bignod{u.l,l-1,u.tim});
if(u.r>r)st.insert(bignod{r+1,u.r,u.tim});
st.insert(bignod{l,r,i});
if(u.tim+1<=i-1)gx[++cqt]=gonxian{u.tim+1,i-1,r-l+1};
continue;
}
st.erase(it2);
for(;it!=ti;){
go[(*it).tim]=max(go[(*it).tim],i);
if((*it).tim+1<=i-1)gx[++cqt]=gonxian{(*it).tim+1,i-1,(*it).r-(*it).l+1};
it2=it++;
st.erase(it2);
}
u=*ti,st.erase(ti);
go[v.tim]=max(go[v.tim],i),go[u.tim]=max(go[u.tim],i);
if(v.tim+1<=i-1)gx[++cqt]=gonxian{v.tim+1,i-1,v.r-l+1};
if(u.tim+1<=i-1)gx[++cqt]=gonxian{u.tim+1,i-1,r-u.l+1};
if(v.l<l)st.insert(bignod{v.l,l-1,v.tim});
if(u.r>r)st.insert(bignod{r+1,u.r,u.tim});
st.insert(bignod{l,r,i});
}
for(it=st.begin();it!=st.end();it++){
bignod u=*it;
if(u.tim+1<=m)gx[++cqt]=gonxian{u.tim+1,m,u.r-u.l+1};
go[u.tim]=max(go[u.tim],m+1);
}
for(int i=m;i;i--){
die[i]=max(die[i],go[i]);
if(pre[i])die[pre[i]]=max(die[pre[i]],die[i]);
}
}