CF1209G Into Blocks

一、题目

点此看题

二、解法

开始反思了,为什么我总是不能达成有效的思考,是题目太难还是我根本没有静下心来??

考虑把原序列分成若干段,每一段都变成一个颜色。那么问题如何分段?我们知道如果某个颜色出现的区间是 \([l_c,r_c]\),那么这个区间一定是要在同一段内的,也就是任意 \(i\in[l_c,r_c)\)\(i\)\(i+1\) 都是不能断开的。

那么我们可以把 \([l_c,r_c)\) 都打上 \(+1\) 的标记,对于最终值为 \(0\) 的位置,我们是可以在这个位置断开的,而且能断开则断开的策略一定是最优的。所以最后我们会获得若干段,答案就是 \(n-\sum\) 每一段内的最大值。

考虑用线段树维护所有断开的位置,我们原本想要在值为 \(0\) 的地方断开,这并不好维护。但是根据套路,我们可以在值为区间最小值的地方断开,这样到了根节点,因为最小值 \(=0\),所以就可以直接拿答案了。

那么我们维护 \(mn_i\) 表示 \(i\) 代表区间内的覆盖次数最小值,设 \(s_i\) 表示中间被断点闭合的区间的最大值之和,\(lc_i,rc_i\) 分别为左右还未闭合的区间的最大值,\(mx_i\) 表示区间最大值,讨论左右儿子 \(mn\) 的关系就可以上传。

我们把每个颜色的出现次数放在它的第一个节点的位置,那么只需要支持单点修改和区间覆盖修改。至于颜色则可以拿个 \(\tt set\) 来维护,时间复杂度 \(O(n\log n)\)

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <set>
using namespace std;
const int M = 200005;
const int N = M<<2;
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,q,a[M],mx[N],lc[N],rc[N],tg[N],mn[N],tr[N];
set<int> s[M];
void down(int i)
{
	if(!tg[i]) return ;
	tg[i<<1]+=tg[i];tg[i<<1|1]+=tg[i];
	mn[i<<1]+=tg[i];mn[i<<1|1]+=tg[i];
	tg[i]=0;
}
void up(int i)
{
	int ls=i<<1,rs=i<<1|1;
	if(mn[ls]<mn[rs])
	{
		tr[i]=tr[ls];lc[i]=lc[ls];
		rc[i]=max(rc[ls],mx[rs]);
	}
	else if(mn[i<<1]>mn[i<<1|1])
	{
		tr[i]=tr[rs];rc[i]=rc[rs];
		lc[i]=max(lc[rs],mx[ls]);
	}
	else
	{
		tr[i]=tr[ls]+tr[rs]+max(rc[ls],lc[rs]);
		lc[i]=lc[ls];rc[i]=rc[rs];
	}
	mx[i]=max(mx[ls],mx[rs]);
	mn[i]=min(mn[ls],mn[rs]);
}
void ins(int i,int l,int r,int id,int c)
{
	if(l==r) {mx[i]=lc[i]=c;return ;}
	int mid=(l+r)>>1;down(i);
	if(mid>=id) ins(i<<1,l,mid,id,c);
	else ins(i<<1|1,mid+1,r,id,c);
	up(i);
}
void cov(int i,int l,int r,int L,int R,int c)
{
	if(L>r || l>R) return ;
	if(L<=l && r<=R) {tg[i]+=c;mn[i]+=c;return ;}
	int mid=(l+r)>>1;down(i);
	cov(i<<1,l,mid,L,R,c);
	cov(i<<1|1,mid+1,r,L,R,c);
	up(i);
}
void modify(int x,int c)
{
	if(s[x].empty()) return ;
	ins(1,1,n,*s[x].begin(),c>0?s[x].size():0);
	cov(1,1,n,*s[x].begin(),*s[x].rbegin()-1,c);
}
signed main()
{
	n=read();q=read();
	for(int i=1;i<=n;i++)
		a[i]=read(),s[a[i]].insert(i);
	for(int i=1;i<=n;i++)
		if(*s[a[i]].begin()==i) modify(a[i],1);
	printf("%d\n",n-tr[1]-lc[1]-rc[1]);
	while(q--)
	{
		int x=read(),y=read(),z=a[x];
		modify(z,-1);s[z].erase(x);modify(z,1);
		modify(y,-1);s[y].insert(x);modify(y,1);
		printf("%d\n",n-tr[1]-lc[1]-rc[1]);a[x]=y;
	}
}
posted @ 2022-05-26 20:57  C202044zxy  阅读(159)  评论(0编辑  收藏  举报