P3411 序列变换
P3411
主打的就是一个正难则反!
问最少的操作次数,其实就是最多的不操作次数。这样说可能有点怪,来个结论:
每个数至多操作一次(自证不难)。
所以本题等价于求最多几个数可以原地不动。显然,原地不动的数在最后序列变成有序的时候必然都是有序的,我们要做的是在原序列中提取一个有序的最长子序列。
好像是……板子?
戳啦,这个序列还有一个性质必须满足:是目标序列的子串。假设我们已经求出了这个合法的子序列,那么子序列的空隙里(不会有人不知道子序列可以是断断续续的吧)就是不能加入到子序列里做贡献的数,它们得被移开。而移开后,这个子序列就会凑到一起,变成目标序列的子串。
而这个子串有什么特点?那就是它们并非单纯的大于小于关系,而是对于一对相邻的数
于是我们可以开一个桶套 vector 存储对应值的下标,从小到大加入队列,过程中判断即可。
得到了最多的不操作次数,再结合上面的结论,答案显而易见。
Code:
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
int n,a[N],l=1,r=1,ans,res,Max;
vector<int>v[N];
deque<int>d;
signed main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&a[i]),v[a[i]].push_back(i),Max=max(Max,a[i]);
for(int i=1;i<=Max;i++){
for(int j=v[i].size()-1;j>=0;j--){
int now=v[i][j];
while(d.size()&&d.front()>now){//最大值在次大值前面
while(d.size()&&a[d.back()]<a[d.front()])d.pop_back();//前面也失效了
d.pop_front();
}
ans=max(ans,(int)d.size()+(int)v[i].size()-j);//目前合法序列长度
}
for(int j=0;j<v[i].size();j++)d.push_front(v[i][j]);
}
printf("%d\n",n-ans);
// system("pause");
return 0;
}
record