【CF1481E】Sorting Books(动态规划)

点此看题面

  • 给定(n)本书,每本书有一个颜色。
  • 每次操作你可以任选一本书移到最右边,问至少多少次操作能够让所有相同颜色的书都连在一起。
  • (nle5 imes10^5)

动态规划

当选中若干移到右边的书之后,我们是可以任意决定它们的顺序的。

因此我们反向思考,去考虑留下来的书需要满足什么样的条件。

然后就发现,除了最右边留下来的一种颜色,其余颜色的书必须要满足连成一块,且这种颜色的书必须完全保留。

必须完全保留,也就意味着原本掺杂在这种颜色之间的所有书都必须右移。

于是我们记录每种颜色最左边的书、最右边的书、书的数量,就可以把每种颜色看成一个区间。

那么对于不在最右边的颜色,就是要满足选出权值总和尽可能大的完整区间,这种东西直接把所有区间按右端点排个序就可以愉快(DP)了。

然后对于最右边的颜色,假设我们已经选出的所有区间最大的右端点为(R),那么我们显然会选取([R+1,n])中出现次数最多的书保留,这直接从后往前扫一遍,开桶维护预处理即可。

代码:(O(nlogn))

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 500000
using namespace std;
int n,a[N+5],f[N+5],Mn[N+5],Mx[N+5],tot[N+5],suf[N+5],cnt[N+5];
struct Data
{
	int l,r,v;I Data(CI a=0,CI b=0,CI c=0):l(a),r(b),v(c){}
	I bool operator < (Con Data& o) Con {return r<o.r;}//所有区间按右端点排序
}s[N+5];
class FastIO
{
	private:
		#define FS 100000
		#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
		#define D isdigit(c=tc())
		char c,*A,*B,FI[FS];
	public:
		I FastIO() {A=B=FI;}
		Tp I void read(Ty& x) {x=0;W(!D);W(x=(x<<3)+(x<<1)+(c&15),D);}
}F;
int main()
{
	RI i;for(F.read(n),i=1;i<=n;++i) F.read(a[i]),!Mn[a[i]]&&(Mn[a[i]]=i),Mx[a[i]]=i;//维护每种颜色最左边的书、最右边的书
	for(i=n;i;--i) suf[i]=max(++cnt[a[i]],suf[i+1]);//倒枚一遍,开桶维护每个后缀最多的书的本数
	RI t=0;for(i=1;i<=n;++i) Mx[i]&&(s[++t]=Data(Mn[i],Mx[i],cnt[i]),0);sort(s+1,s+t+1);//建区间,然后排序
	RI p=1;for(i=1;i<=n;f[i]=max(f[i],f[i-1]),++i) W(s[p].r==i) f[i]=max(f[i],f[s[p].l-1]+s[p].v),++p;//双指针维护DP
	RI ans=0;for(i=1;i<=n;++i) ans=max(ans,f[i]+suf[i+1]);return printf("%d
",n-ans),0;//枚举最大的右端点统计答案
}
败得义无反顾,弱得一无是处
原文地址:https://www.cnblogs.com/chenxiaoran666/p/CF1481E.html