CF1481E Sorting Books

23 篇文章 0 订阅

题目大意

你现在要整理书架上的 n n n 本书,每本书有一个颜色 a i a_i ai,当每种颜色的书都摆在一起时书架上便整齐了,你每次可以将一本书放到序列最右端,问使书架上整齐的最小操作数。

Sol

容易得到,答案最大是 n n n次,因为我们可以随意地操作序列 n n n次,使序列从小到大就可以了,然后对于答案,我们就只需要求最多可以剩下几个书动,这样子 a n s ans ans就是 n n n减去不移动的最大值。
我们可以设立状态 f i f_i fi表示 [ i , n ] [i,n] [i,n]最大的不用动数量,因为每次操作可以放到最后面,因此,我们很容易想打这种状态。然后考虑转移发现除了最右边留下来的一种值,每类元素必须要连成一块且完全保留。必须完全保留,也就意味着原本在这种值之间的所有元素都必须右移。于是我们记录每种值最左边的元素、最右边的元素、元素的数量,就可以把每类元素看成一个区间。那么对于不在最右边的值,就是要满足选出权值总和尽可能大的完整区间,这种东西直接把所有区间按右端点排个序就可以 DP 了。
然后对于最右边的值,假设我们已经选出的所有区间最大的右端点为 R,那么我们显然会选取 [R+1,n] 中出现次数最多的元素保留,这直接从后往前扫一遍,开桶维护预处理即可。可以做到 O ( n ) O(n) O(n)
f i = m a x { f i + 1 if  i∈[1,n) c n t i if  i=l[a[i]] ; c n t i + f r [ a [ i ] ] + 1 if  i!=l[a[i]] f_i = max\begin{cases}f_{i+1}\quad \text {if \textcolor{orange}{i∈[1,n)}} \\ cnt_i\quad \text{if \textcolor{orange}{i=l[a[i]]};}\\ cnt_i+f_{r[a[i]]+1} \quad\text{if \textcolor{orange}{i!=l[a[i]]}} \end{cases} fi=max fi+1if i∈[1,n)cntiif i=l[a[i]];cnti+fr[a[i]]+1if i!=l[a[i]]

Code

#include<bits/stdc++.h>
#define I inline
#define LL long long
#define inf 0x3f3f3f3f
#define LB long double
#define RI register int
#define N 1000100
#define debug puts("debug");
using namespace std;
I int read()
{
	int res=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-f;ch=getchar();}
	while(isdigit(ch))res=(res<<1)+(res<<3)+(ch&15),ch=getchar();
	return res*f;
}
int n,a[N],ans,l[N],r[N],cnt[N],f[N];
int main()
{ 
    memset(l,inf,sizeof l);
	n=read();for(RI x,i=1;i<=n;i++)a[i]=read(),l[a[i]]=min(l[a[i]],i),r[a[i]]=max(r[a[i]],i);
	for(RI i=n;i>=1;i--){
		cnt[a[i]]++;f[i]=f[i+1];
		if(i==l[a[i]])f[i]=max(f[i],f[r[a[i]]+1]+cnt[a[i]]);
		else f[i]=max(f[i],cnt[a[i]]);
	}printf("%d",n-f[1]);return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值