CF1764H Doremy's Paint 2
题意
给定一个数组 \(a\),初始 \(a_i=i\)。定义对一个区间 \([l,r]\) 操作为将 \(a_{l+1}~a_r\) 赋值为 \(a_l\)。
给定一个区间组成的序列 \(b\),对 \(b\) 的每一个长度为 \(k\) 的区间求从左到右依次执行区间中的区间操作后 \(a\) 不同元素的种类数。
思路
思路 from huangzirui 大佬。
首先用去掉那些区间操作没有涉及的颜色的贡献,这一部分相当于是计算一堆区间并集的大小,用区间加减线段树维护最小值个数解决。
之后只有区间的左端点会留到最后,我们可以看成 \(a\) 本来没有颜色,区间操作是染色,再对颜色计数。
这道题难以处理的主要原因是前面的操作会影响到后面的操作的颜色。
如果我们把这写区间操作改为第 \(i\) 个操作的染的颜色都是固定的 \(i\),这题好做:
只需要考虑这个区间染的颜色是否会被后面的区间全部覆盖,倒序处理,用区间染色线段树维护区间最大值,就可以知道一个区间最后是什么时间被全部覆盖。
现在一个区间染的颜色会因为前面的区间的颜色而改变,这种区间的依赖关系我们事实上是可以很好的描述的:
一个区间 \(i\) 染的颜色相当于在它之前上一个覆盖它左端点的区间 \(t_i\) 的颜色。\(t_i\to i\) 这是一个树形关系,形成了一个森林,其中一棵树颜色相同。
既然是对颜色计数,我们考虑只在树根处计算贡献。一颗树的颜色被完全覆盖的时间,就是这棵树的所有节点都被后面的区间覆盖完的时间,也就是每一个节点被后面区间完全覆盖的时间的 \(\max\),记为 \(f_x\)。
于是对于一个区间 \(x\) 的颜色,设考虑的操作区间为 \([L,R]\),那么 \(t_x<L\) 时区间 \(x\) 是根,当 \(R<f_x\) 时这棵树有节点没有被完全覆盖,该颜色造成贡献。
于是一个颜色造成贡献是一段区间,差分统计即可。
#include <cstdio>
#include <algorithm>
#pragma GCC optimize(2,3,"Ofast")
#define lc (p<<1)
#define rc (p<<1|1)
using namespace std;
int read(){
char c=getchar();int x=0;
while(c<48||c>57) c=getchar();
do x=(x<<1)+(x<<3)+(c^48),c=getchar();
while(c>=48&&c<=57);
return x;
}
const int N=400003,INF=0x3f3f3f3f;
int n,m,k,lim;
int ql[N],qr[N];
namespace tree{
int mn[N<<2],cnt[N<<2],tag[N<<2];
void proc(int p,int v){tag[p]+=v;mn[p]+=v;}
void pushdown(int p){
if(tag[p]){proc(lc,tag[p]);proc(rc,tag[p]);tag[p]=0;}
}
void pushup(int p){
if(mn[lc]<mn[rc]){mn[p]=mn[lc];cnt[p]=cnt[lc];return;}
mn[p]=mn[rc];cnt[p]=cnt[rc];
if(mn[lc]==mn[rc]) cnt[p]+=cnt[lc];
}
void build(int p=1,int l=1,int r=n){
cnt[p]=r-l+1;mn[p]=0;
if(l==r) return;
int mid=(l+r)>>1;
build(lc,l,mid);
build(rc,mid+1,r);
}
void modify(int ll,int rr,int v,int p=1,int l=1,int r=n){
if(ll<=l&&r<=rr) return proc(p,v);
int mid=(l+r)>>1;
pushdown(p);
if(ll<=mid) modify(ll,rr,v,lc,l,mid);
if(rr>mid) modify(ll,rr,v,rc,mid+1,r);
pushup(p);
}
}
namespace color{
int mx[N<<2],tag[N<<2];
void proc(int p,int v){mx[p]=tag[p]=v;}
void pushup(int p){mx[p]=max(mx[lc],mx[rc]);}
void pushdown(int p){
if(~tag[p]){proc(lc,tag[p]);proc(rc,tag[p]);tag[p]=-1;}
}
void build(int v,int p=1,int l=1,int r=n){
mx[p]=v;tag[p]=-1;
if(l==r) return;
int mid=(l+r)>>1;
build(v,lc,l,mid);
build(v,rc,mid+1,r);
}
void modify(int ll,int rr,int v,int p=1,int l=1,int r=n){
if(ll<=l&&r<=rr) return proc(p,v);
int mid=(l+r)>>1;
pushdown(p);
if(ll<=mid) modify(ll,rr,v,lc,l,mid);
if(rr>mid) modify(ll,rr,v,rc,mid+1,r);
pushup(p);
}
int query(int ll,int rr,int p=1,int l=1,int r=n){
if(ll<=l&&r<=rr) return mx[p];
int mid=(l+r)>>1;
pushdown(p);
if(rr<=mid) return query(ll,rr,lc,l,mid);
if(ll>mid) return query(ll,rr,rc,mid+1,r);
return max(query(ll,rr,lc,l,mid),query(ll,rr,rc,mid+1,r));
}
}
int ans[N],t[N],f[N],pre[N];
int main(){
n=read();m=read();k=read();
for(int i=1;i<=m;++i){
ql[i]=ql[i+m]=read();
qr[i]=qr[i+m]=read();
}
lim=m+k-1;
tree::build();
for(int i=1;i<k;++i) tree::modify(ql[i],qr[i],1);
for(int i=1;i<=m;++i){
tree::modify(ql[i+k-1],qr[i+k-1],1);
if(!tree::mn[1]) ans[i]=tree::cnt[1];
tree::modify(ql[i],qr[i],-1);
}
color::build(0);
for(int i=1;i<=lim;++i){
t[i]=color::query(ql[i],ql[i]);
color::modify(ql[i],qr[i],i);
}
color::build(INF);
for(int i=lim;i;--i){
f[i]=max(f[i],color::query(ql[i],qr[i]));
f[t[i]]=max(f[t[i]],f[i]);
color::modify(ql[i],qr[i],i);
}
for(int i=1;i<=lim;++i){
int l=max(i-k+1,t[i]+1),r=min(i,f[i]-k);
if(l<=r){
++pre[l];
if(r<m) --pre[r+1];
}
}
for(int i=1;i<=m;++i) pre[i]+=pre[i-1],printf("%d ",ans[i]+=pre[i]);
putchar('\n');
return 0;
}