专栏文章

题解:CF2146E Yet Another MEX Problem

CF2146E题解参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@mins2zk4
此快照首次捕获于
2025/12/02 07:26
3 个月前
此快照最后确认于
2025/12/02 07:26
3 个月前
查看原文
  • 我们考虑对于每个右端点 ii,选择枚举 [l,i][l,i] 的区间 MEX=x\text{MEX}=x
  • 注意到直接枚举 xx 是不容易做的,因为我们没办法钦定 [0,x)[0,x) 全部出现;但是只要 xx 没有出现,就一定有 MEXx\text{MEX}\leq x,而此时 xx 的贡献不优,因此这么做不会算大贡献。
  • 现在考虑如何维护每个 xx 的贡献。设 xx 上一次出现的位置为 pp(特别地,没有出现则为 00),那么由于 xx 不能出现,所以有 l>pl>p。此时显然令 l=p+1l=p+1 最优,贡献就是 k=p+1i[ak>x]\sum\limits_{k=p+1}^i [a_k>x]
  • 发现这个贡献形式非常平凡,每次加入 aia_i 的时候对于 x<aix<a_ixx 贡献加一即可。另外加入的时候还要注意特殊更新 x=aix=a_i 的情况,将它的贡献置为 00
  • 现在需要支持的操作是区间加,单点赋值和全局查询。显然可以线段树维护之,复杂度 O(nlogn)O(n\log n)

好久没写线段树了。
CPP
#include<bits/stdc++.h>
using namespace std;
const int N = 3e5 + 7;
int n,m;
int tr[N*4],tag[N*4];
void pushdown(int x){
	tag[x*2] += tag[x];
	tag[x*2+1] += tag[x];
	tr[x*2] += tag[x];
	tr[x*2+1] += tag[x];
	tag[x] = 0;
}
void build(int x,int l,int r){
	tr[x] = tag[x] = 0;
	if(l==r){
		return;
	}
	int mid = (l+r)/2;
	build(x*2,l,mid);
	build(x*2+1,mid+1,r);
	tr[x] = max(tr[x*2],tr[x*2+1]);
}
void update(int x,int l,int r,int L,int R){
	if(L>r || l>R) return;
	if(l<=L && R<=r){
		tr[x] += 1;
		tag[x] += 1;
		return;
	}
	pushdown(x);
	int mid = (L+R)/2;
	update(x*2,l,r,L,mid);
	update(x*2+1,l,r,mid+1,R);
	tr[x] = max(tr[x*2],tr[x*2+1]);
}
void mdf(int x,int l,int r,int pos){
	if(l==r){
		tr[x] = 0;
		return;
	}
	pushdown(x);
	int mid = (l+r)/2;
	if(pos<=mid) mdf(x*2,l,mid,pos);
	else mdf(x*2+1,mid+1,r,pos);
	tr[x] = max(tr[x*2],tr[x*2+1]);
}
void solve(){
	cin>>n;
	build(1,0,n);
	for(int i=1;i<=n;++i){
		int x;cin>>x;
		update(1,0,x-1,0,n);
		mdf(1,0,n,x);
		cout<<tr[1]<<' ';
	}
	cout<<'\n';
}
signed main(){
	ios::sync_with_stdio(0);
	cin.tie(nullptr);
	cout.tie(nullptr);
	int T;cin>>T;
	for(int _=1;_<=T;++_){
		solve();
	}
	return 0;
}

评论

0 条评论,欢迎与作者交流。

正在加载评论...