题解:吉司机线段树
题意
给定一个序列 \(a\) 和 \(m\) 个操作:
-
1 x:将 \(a_x\) 清零。 -
2 x:将 \(1\le i\le n\) 的所有 \(a_i\) 全部赋值为 \(\max(a_i,x)\)。
此外,还有 \(q\) 个删除操作,第 \(i\) 次会将第 \(p_i\) 个操作删除,删除是永久的。每次删除过后,请求出按顺序进行完剩余 \(m-i\) 个操作后,\(\sum\limits_{i=1}^n a_i\) 的值。
\(1\le n,m,q\le 3\times 10^5\),1.5s。
题解
删除太困难了,显然把删除倒过来转化为插入。
插入也不好做,因为你插入的时间是不固定的,所以考虑在时间轴建立数据结构(??),维护他对前后的操作的影响。
观察到一个位置只有最后一次清零操作有用。那我们考虑维护一个线段树,记录 \(\text{cnt}\) 表示当前区间内有多少个生效的清零操作,\(\text{mx}\) 表示当前区间的左端点到 \(m\) 的最大的取 \(\max\) 操作是多少。那显然答案可以表示为所有叶子的 \(\text{cnt}\times\text{mx}\) 之和,记为 \(\text{sum}\)。
考虑一次操作会对其造成什么影响。分两个操作来看:
- 在时间 \(i\) 插入一个操作
1 x。
记位置 \(x\) 最后一次清零操作的时间为 \(\text{lst}_x\),则当 \(\text{lst}_x>i\) 时,这次操作不可能成为有效的清零操作。否则将 \(\text{cnt}_{\text{lst}_x} \to 0\),\(\text{cnt}_i \to 1\),\(\text{lst}_x \to i\)。单点修改很好维护。
- 在时间 \(i\) 插入一个操作
2 x。
因为 \(\text{mx}\) 维护的是后缀的 \(\max\),故它是单调不递增的。那一次修改,会将区间 \([\text{pos},i]\) 这段区间的 \(\text{mx}\) 全部赋值成 \(x\),其中 \(\text{pos}\) 是满足 \(\text{mx}_\text{pos}<x\) 的最小的时间。这个可以线段树二分出来,而区间赋值也是简单的。
最终要求的答案就是线段树根节点的 \(\text{sum}\)。复杂度 \(\mathcal O(n\log n)\)(假设 \(n,m,q\) 同阶)。
看似这道题做完了,但是还有一个问题:初始的 \(a_i\) 怎么办?如果一次清零操作都没有可能会对答案产生影响。
当然你可以将这些分开维护,但这没必要。我们可以将 \(a\) 排序,假设排序后的 \(a_i\) 的原始位置是 \(\text{id}_i\),那么可以依次在最开始加入 \(2n\) 个操作:第 \(2i-1\) 个操作插入 1 id[i],第 \(2i\) 个操作插入 2 a[i]。容易证明这是对的。
代码
#define int long long
int n,m,q,id[1000005],x[1000005],y[1000005],p[1000005],ans[1000005],lst[1000005];
pii a[1000005];
bool vis[1000005];
struct segtree{
#define ls (x<<1)
#define rs (x<<1|1)
#define mid (l+r>>1)
int tr[4000005],cnt[4000005],mx[4000005],lzy[4000005];
void pushup(int x){tr[x]=tr[ls]+tr[rs],cnt[x]=cnt[ls]+cnt[rs],mx[x]=mx[ls];}
void pushdown(int x){
if(lzy[x])
mx[ls]=lzy[ls]=lzy[x],tr[ls]=mx[ls]*cnt[ls],
mx[rs]=lzy[rs]=lzy[x],tr[rs]=mx[rs]*cnt[rs],lzy[x]=0;
}
void updcnt(int x,int l,int r,int q,int k){
if(l==r)return cnt[x]=k,tr[x]=cnt[x]*mx[x],void();
pushdown(x);
if(q<=mid)updcnt(ls,l,mid,q,k);
else updcnt(rs,mid+1,r,q,k);
pushup(x);
}
void updmx(int x,int l,int r,int ql,int qr,int k){
if(ql<=l&&r<=qr)return mx[x]=lzy[x]=k,tr[x]=cnt[x]*mx[x],void();
pushdown(x);
if(ql<=mid)updmx(ls,l,mid,ql,qr,k);
if(mid<qr)updmx(rs,mid+1,r,ql,qr,k);
pushup(x);
}
int query(int x,int l,int r,int ql,int qr,int k){
if(l==r)return mx[x]>=k?-1:l;
pushdown(x);
if(qr<=mid)return query(ls,l,mid,ql,qr,k);
if(mid<ql)return query(rs,mid+1,r,ql,qr,k);
if(mx[rs]<k){
int tmp=query(ls,l,mid,ql,qr,k);
return ~tmp?tmp:mid+1;
}
return query(rs,mid+1,r,ql,qr,k);
}
}seg;
void solve(){
cin>>n>>m>>q,m+=n*2;
fo(i,1,n)cin>>a[i].fi,a[i].se=i;
sort(a+1,a+1+n),reverse(a+1,a+1+n);
fo(i,1,n)x[i*2-1]=1,y[i*2-1]=a[i].se,x[i*2]=2,y[i*2]=a[i].fi;
fo(i,n*2+1,m)cin>>x[i]>>y[i];
fo(i,1,q)cin>>p[i],p[i]+=2*n,vis[p[i]]=1;
fo(i,1,m)if(!vis[i]){
if(x[i]==1){
if(lst[y[i]])seg.updcnt(1,1,m,lst[y[i]],0);
seg.updcnt(1,1,m,i,1),lst[y[i]]=i;
}
else{
int pos=seg.query(1,1,m,1,i,y[i]);
if(~pos)seg.updmx(1,1,m,pos,i,y[i]);
}
}
fd(i,q,1){
ans[i]=seg.tr[1];
if(x[p[i]]==1){
if(p[i]>lst[y[p[i]]])
seg.updcnt(1,1,m,lst[y[p[i]]],0),seg.updcnt(1,1,m,p[i],1),lst[y[p[i]]]=p[i];
}
else{
int pos=seg.query(1,1,m,1,p[i],y[p[i]]);
if(~pos)seg.updmx(1,1,m,pos,p[i],y[p[i]]);
}
}
fo(i,1,q)cout<<ans[i]<<'\n';
}

浙公网安备 33010602011771号