CF1905D Cyclic MEX 题解

本文介绍了一种使用线段树数据结构解决动态区间赋值问题的方法,通过计算相邻状态之间的变化量,尤其是mex值的变化,来优化复杂度至O(nlogn)。文章详细阐述了如何构建线段树并进行区间操作,最终求解特定状态的mex值之和。
摘要由CSDN通过智能技术生成

分析

乱搞题。

右移若干次很显然可以破环成链,然后 l , r l,r l,r 指针在这个长度为 2 n 2n 2n 的序列上右移。每种状态的答案就是 [ l , r ] [l,r] [l,r] 的答案。

考虑相邻两种状态的变化量。设上一次 l ′ l' l 指向的值为 p l ′ p_{l'} pl,则到当前状态的变化量就是: − mex ⁡ ( p l ′ ) − s u m + c n t × p l ′ + n -\operatorname{mex}(p_{l'})-sum + cnt \times p_{l'}+n mex(pl)sum+cnt×pl+n。第一项和最后一项很显然,就是 l ′ l' l 从开头变到结尾了。第二项 s u m sum sum 表示 mex ⁡ \operatorname{mex} mex 值比 p l ′ p_{l'} pl 大的和。因为在现在的前 n − 1 n-1 n1 项中 mex ⁡ \operatorname{mex} mex 值是不可能超过 p l ′ p_{l'} pl 的( p l ′ p_{l'} pl 空出来了)。第三项的 c n t cnt cnt 就是比 p l ′ p_{l'} pl 大的值的数量。

这个变化量用线段树乱搞就行了,相当于是区间赋值。在线段树上二分,统计一下最大值和最小值即可。最后的求某种状态 mex ⁡ \operatorname{mex} mex 值之和就是区间 [ l , r ] [l,r] [l,r] 的和。复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn)

代码

#include<bits/stdc++.h>
using namespace std;
//#define int long long
#define re register
#define il inline
 
const int N=1e6+5,M=1e7+5;
int n,p[N];
bool cnt[N];
struct tree{
	long long l,r,sum,lz,mx,mi;
}tr[M];
 
il void up(int now){
	tr[now].sum=tr[now<<1].sum+tr[now<<1|1].sum;
	tr[now].mx=max(tr[now<<1].mx,tr[now<<1|1].mx);
	tr[now].mi=min(tr[now<<1].mi,tr[now<<1|1].mi);
	return ;
}
il void down(int now){
	if(tr[now].lz!=-1){
		tr[now<<1].lz=tr[now<<1|1].lz=tr[now].lz;
		tr[now<<1].sum=(tr[now<<1].r-tr[now<<1].l+1)*tr[now].lz;
		tr[now<<1|1].sum=(tr[now<<1|1].r-tr[now<<1|1].l+1)*tr[now].lz;
		tr[now<<1].mx=tr[now<<1|1].mx=tr[now].lz;
		tr[now<<1].mi=tr[now<<1|1].mi=tr[now].lz;
		tr[now].lz=-1;
	}
	return ;
}
il void build(int now,int l,int r){
	tr[now].l=l,tr[now].r=r,tr[now].sum=0,tr[now].lz=-1,tr[now].mx=0,tr[now].mi=1e18;
	if(l==r) return ;
	int mid=l+r>>1;
	build(now<<1,l,mid),build(now<<1|1,mid+1,r);
	return ;
}
il void insert(int now,int l,int r,int k){
	if(tr[now].l>=l&&tr[now].r<=r){
		tr[now].lz=k,tr[now].sum=(tr[now].r-tr[now].l+1)*k;
		tr[now].mx=k,
		tr[now].mi=k;
		return ;
	}
	down(now);
	int mid=tr[now].l+tr[now].r>>1;
	if(l<=mid) insert(now<<1,l,r,k);
	if(mid<r) insert(now<<1|1,l,r,k);
	up(now);return ;
}
il void insert2(int now,int l,int r,int k){
	if(tr[now].l>=l&&tr[now].r<=r){
		down(now);
		if(tr[now].mi>k){
			tr[now].lz=k,tr[now].sum=(tr[now].r-tr[now].l+1)*k;
			tr[now].mx=tr[now].mi=k;return ;
		}	
		if(tr[now<<1].mx>k) insert2(now<<1,l,r,k);
		if(tr[now<<1|1].mx>k) insert2(now<<1|1,l,r,k);
		up(now);return ;
	}
	down(now);
	int mid=tr[now].l+tr[now].r>>1;
	if(l<=mid&&tr[now<<1].mx>k) insert2(now<<1,l,r,k);
	if(mid<r&&tr[now<<1|1].mx>k) insert2(now<<1|1,l,r,k);
	up(now);return ;
}
il long long query(int now,int l,int r){
	if(tr[now].l>=l&&tr[now].r<=r) return tr[now].sum;
	down(now);
	int mid=tr[now].l+tr[now].r>>1;
	long long ans=0;
	if(l<=mid) ans+=query(now<<1,l,r);
	if(mid<r) ans+=query(now<<1|1,l,r);
	up(now);
	return ans;
}
 
il void solve(){
	scanf("%d",&n);
	for(re int i=1;i<=n;++i) scanf("%d",&p[i]);
	build(1,1,n*2);int lst=0;
	for(re int i=1;i<=n;++i){
		cnt[p[i]]=1;
		while(cnt[lst]) ++lst;
		insert(1,i,i,lst);
	}
	for(re int i=0;i<=n;++i) cnt[i]=0;
	long long maxx=query(1,1,n);
	int l=1,r=n;
	for(re int i=1;i<=n;++i){
		int now=p[l];
		int L=l+1,R=r;
		insert2(1,L,R,now);
		++l,++r;
		insert(1,r,r,n);
		maxx=max(maxx,query(1,l,r));
	}
	cout<<maxx<<"\n";return ; 
}
 
signed main(){
	int t;cin>>t;while(t--)
	solve();
	return 0;
}
  • 17
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

harmis_yz

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值