【题解】CF1479B1 Painting the Array I
题意简述
定义连续段
给定长度为
数据范围:
分析 + 题解
题目可以看作有两个队列
设当前
-
若
z 与x,y 中的至少一个相等,我们将z 加入当前队尾不为z 的队列。(若z=x=y ,可任意选择) -
若
z 不与x,y 中的任意一个相等,我们将z 加入s 当且仅当next(x)<next(y) 。其中next(x) 表示x 这种值下一次出现的位置,若之后没有出现next(x)=n+1 。
感性理解:
-
如果不这样做的话,那之后一次对于相等的
z 这样做,答案相同,效果等价;(效果指操作后x=y=z )若之后一次都没有对相等的z 这样做,答案减少1 ; -
为了让
x 和a_{next(x)} 不要连成一段,用与它们都不同的z 隔开,而y 和a_{next(y)} 之后更有可能被其他元素隔开。
理性证明:
分类讨论即可发现这样的策略不会比其他的答案差,比较冗长,此处略。(可以看一下官方题解的证明,建议看 "An Intutive Proof",更加直观)
代码
预处理 Next 数组然后模拟即可。
#include<bits/stdc++.h>
using namespace std;
const int max_n=1e5+5;
int a[max_n],id[max_n],Next[max_n];
typedef pair<int,int> P;//前一个是元素的值,后一个是元素的编号
vector<P> w,b;
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;++i)
{
scanf("%d",a+i);
id[i]=n+1;//注意初始化
}
for(int i=n;i>=1;--i)
{
Next[i]=id[a[i]];
id[a[i]]=i;
}
w.push_back(P(0,n+1));
b.push_back(P(0,n+1));
int ans=0;
for(int i=1;i<=n;++i)
{
if(a[i]==w.back().first)
{
if(a[i]!=b.back().first)
++ans;
b.push_back(P(a[i],Next[i]));
}
else if(a[i]==b.back().first)
{
++ans;
w.push_back(P(a[i],Next[i]));
}
else
{
++ans;
if(w.back().second<b.back().second)
w.push_back(P(a[i],Next[i]));
else
b.push_back(P(a[i],Next[i]));
}
}
printf("%d\n",ans);
return 0;
}