[ABC470G] ΣШX 题解

[ABC470G] ΣШX 题解

题意

给定一个长度为 NNN 的序列 AAA0≤Ai≤N0 \le A_i \le N0AiN),求所有区间的 mex⁡\operatorname{mex}mex(未出现的最小非负整数)。

思路

虽然本题非常典,但由于我没做过所以记录一下。

考虑对于一个右端点 rrr,有多少个 lll 满足 mex⁡(l,r)=k\operatorname{mex}(l,r)=kmex(l,r)=k。设 poskpos_kposk 表示当前 kkk 最后一次出现的位置(没有则为 000),则有 l≥max⁡0≤i≤k−1poskl \ge \max_{\substack{0 \le i \le k-1}}pos_klmax0ik1posk,即 posposposk−1k-1k1 项的前缀最小值。那么对于每一个 kkk,满足 mex⁡(l,r)≥k+1\operatorname{mex}(l,r)\ge k+1mex(l,r)k+1lll 的数量即为 posposposkkk 的前缀最小值。

由此可得,当前所有 kkk 的前缀最小值的和即为当前右端点的贡献。

模拟一下:

A=(2,1,0,3)A=(2,1,0,3)A=(2,1,0,3)

初始:

pos0123
-0000
premin0123
-0000

r=1r=1r=1 时:

pos0123
-0010
premin0123
-0000

ans1=0+0+0+0=0ans_1=0+0+0+0=0ans1=0+0+0+0=0

r=2r=2r=2 时:

pos0123
-0210
premin0123
-0000

ans2=0+0+0+0=0ans_2=0+0+0+0=0ans2=0+0+0+0=0

r=3r=3r=3 时:

pos0123
-3210
premin0123
-3210

ans3=3+2+1+0=6ans_3=3+2+1+0=6ans3=3+2+1+0=6

r=4r=4r=4 时:

pos0123
-3214
premin0123
-3211

ans4=3+2+1+1=7ans_4=3+2+1+1=7ans4=3+2+1+1=7

最终答案即为 ans=0+0+6+7=13ans=0+0+6+7=13ans=0+0+6+7=13

如果暴力维护 preminpreminpremin 显然是 O(n2)O(n^2)O(n2) 的,需要优化。

考虑线段树维护区间前缀最大值和,难点在于如何合并。

想要将左区间和右区间合并,首先左区间的贡献是不变的,考虑右区间的贡献。

https://cdn.luogu.com.cn/upload/image_hosting/z2e9snx8.png

图中红色部分是有用的贡献,蓝色是无用的贡献,则右区间的贡献相当于在前面加一个左区间最小值时的答案。

findc(p,v)findc(p,v)findc(p,v) 为当前在线段树上的 ppp 节点,表示的区间为 [lp,rp][l_p,r_p][lp,rp],前面有一个数 vvv 时的贡献。设 lc(p),rc(p)lc(p),rc(p)lc(p),rc(p)ppp 的左右节点,mnp,valpmn_p,val_pmnp,valp 分别表示节点所表示的区间的最小值以及答案。

mnlc(p)<vmn_{lc(p)}<vmnlc(p)<v 时说明 vvv 可能会对 vallc(p)val_{lc(p)}vallc(p) 产生影响,但不会对 valrc(p)val_{rc(p)}valrc(p) 产生影响(注:此处的 valrc(p)val_{rc(p)}valrc(p) 是合并后的,已经算了 lc(p)lc(p)lc(p) 的影响,在代码中需要写成 valp−vallc(p)val_p-val_{lc(p)}valpvallc(p)),即 findc(p,v)←findc(lc(p),c)+valrc(p)findc(p,v) \leftarrow findc(lc(p),c)+val_{rc(p)}findc(p,v)findc(lc(p),c)+valrc(p)

否则 vvv 会将 vallc(p)val_{lc(p)}vallc(p) 覆盖为 (rlc(p)−llc(p)+1)×v(r_{lc(p)}-l_{lc(p)}+1)\times v(rlc(p)llc(p)+1)×v,会对右区间产生影响,所以 findc(p,v)←(rlc(p)−llc(p)+1)×v+findc(rc(p),c)findc(p,v) \leftarrow (r_{lc(p)}-l_{lc(p)}+1)\times v+findc(rc(p),c)findc(p,v)(rlc(p)llc(p)+1)×v+findc(rc(p),c)

最后特判一下叶子节点即可。

findc(p,v)={min⁡(valp,v)p is a leaf nodefindc(lc(p),c)+valrc(p)mnlc(p)<v(rlc(p)−llc(p)+1)×v+findc(rc(p),c)other findc(p,v)= \begin{cases} \min(val_p,v) & p\ is\ a\ leaf\ node\\ findc(lc(p),c)+val_{rc(p)} & mn_{lc(p)}<v\\ (r_{lc(p)}-l_{lc(p)}+1)\times v+findc(rc(p),c) & other \end{cases} findc(p,v)=min(valp,v)findc(lc(p),c)+valrc(p)(rlc(p)llc(p)+1)×v+findc(rc(p),c)p is a leaf nodemnlc(p)<vother

findcfindcfindc 的时间复杂度为 O(log⁡n)O(\log n)O(logn),即一次 pushuppushuppushup 的时间复杂度为 O(log⁡n)O(\log n)O(logn),修改一个值的时间复杂度为 O(log⁡2n)O(\log^2 n)O(log2n),一共 NNN 个元素,总时间复杂度为 O(nlog⁡2n)O(n \log^2 n)O(nlog2n)

代码

#include<bits/stdc++.h>
#define lc(p) ((p)<<1)
#define rc(p) ((p)<<1|1) 
using namespace std;
typedef long long ll;
int n,a[300010];
struct N{
	ll mn,c;
}tr[1200010];
ll findc(int p,int l,int r,ll v){
	if(l==r)return min(tr[p].mn,v); 
	int mid=(l+r)>>1;
	if(tr[lc(p)].mn<v)return findc(lc(p),l,mid,v)+tr[p].c-tr[lc(p)].c;
	else return (mid-l+1)*v+findc(rc(p),mid+1,r,v);
}
void pushup(int p,int l,int r){
	int mid=(l+r)>>1;
	tr[p].c=tr[lc(p)].c+findc(rc(p),mid+1,r,tr[lc(p)].mn);
	tr[p].mn=min(tr[lc(p)].mn,tr[rc(p)].mn);
}
void change(int p,int l,int r,int x,int v){
	if(l==r){
		tr[p].mn=tr[p].c=v;
		return ;
	}
	int mid=(l+r)>>1;
	if(x<=mid)change(lc(p),l,mid,x,v);
	else change(rc(p),mid+1,r,x,v);
	pushup(p,l,r);
}
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	cin>>n;
	ll ans=0;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		change(1,0,n,a[i],i);
		ans+=tr[1].c;
	}
	cout<<ans;
	return 0; 
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值