题意:
给你n个数,你每次可以将这个数组里的一个数放到最前面或者放到最后面,问你最后要使得这个数组变成升序,问你最少需要操作几次。
题解:
我没想到一个很关键的点,于是就一直想不出来,先介绍一下看来的一个大神的操作
首先将这个数组离散化,然后可以知道,我们选取的不动序列应该是这样的:
假设我们取得值域是x-y,那么值在x-y之间的所有数都应该取并且他们一定是已经有序了的,值是x-1的数的序列应当取在最小下标的x之前的一个前缀,值是y+1的数的序列应当取在最大下标的y之后的一个后缀。
操作1
那么这个大神的操作是这样的,以的关键字排序之后,这个数组是先以值从小到大排序,值相同以下标从大到小排序。
然后从小到大去做,假设当前的数的值是x,那么用一个栈(应该是啥都可以)来保存已经访问过的值为x的数的下标。
然后用一个set来维护值<x的合法的序列。
这时候很厉害的点就来了:
用l维护最左端可以到哪里,然后在set中由于是按照下标从大到小排序的,那么当set的下标大于当前的数的下标的话,就把最左端的值给删掉,直到序列合法为止。
这里就相当于一直在维护上面图片中那个蓝色的前缀。
#include<bits/stdc++.h>
using namespace std;
#define pa pair<int,int>
const int N=2e5+5;
set<int>s;
int st[N],top;
pa a[N];
int main()
{
int t;
scanf("%d",&t);
while(t--){
int n,x;
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&x),a[i]={x,-i};
sort(a+1,a+1+n);
int ans=0,l=1;
for(int i=1;i<=n;i++){
if(a[i].first!=a[i-top].first)
while(top)
s.insert(st[top--]);
while(!s.empty()&&*s.begin()<a[i].second)
s.erase(s.find(a[l++].second));
st[++top]=a[i].second;
ans=max(ans,top+(int)s.size());
}
printf("%d\n",n-ans);
s.clear(),top=0;
}
return 0;
}
操作2
那么我怎么能看完大神的操作而没有一点自己的想法呢?
我想到可以类似上面那样,用dp数组来维护黑色和蓝色的段长,
那么此时枚举到的位置如果是两个数的临界点,就有两种可能:
1.
a[i].second>a[i-1].second
也就是后面数的最前面>前面数的最后面位置,那么这两个数就可以相当于在上面图中x-y的值之间,所以dp[i]=dp[i-1]+1
2.
那么就是说前面的数是蓝色的区域,后面的数是黑色的区域,于是就暴力找一下前面数的最后一个在a[i].second之前的数即可。
然后我用一个set维护每个数的所有合法位置,如果这个数的值不等于下一个数的值,那么就说明到了可能是蓝色和橙色区域的交界处,看看橙色后缀的大小即可。
这时候有一个很重要的点就是,可能没有黑色区域的时候答案是最大的。
#include<bits/stdc++.h>
using namespace std;
#define pa pair<int,int>
const int N=2e5+5;
set<int>pos[N];
pa a[N];
int b[N],dp[N];
int main()
{
int t;
scanf("%d",&t);
for(int tim=1;tim<=t;tim++){
int n,x;
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&x),a[i]={x,i},b[i]=x;
a[n+1]={0,0};
sort(b+1,b+1+n);
int all=unique(b+1,b+1+n)-b-1;
for(int i=1;i<=n;i++){
a[i].first=lower_bound(b+1,b+1+all,a[i].first)-b;
pos[a[i].first].insert(i);
}
sort(a+1,a+1+n);
int ans=0,l=1;
for(int i=1;i<=n;i++){
if(a[i].first!=a[i-1].first){
l=i;
if(a[i].second>a[i-1].second)
dp[i]=dp[i-1]+1;
else{
int r=i-1,v=a[i-1].first;
while(a[r].first==v&&a[r].second>a[i].second)
r--;
while(a[r].first==v)
r--,dp[i]++;
dp[i]++;
}
}
else
dp[i]=dp[i-1]+1;
int ne=a[i].first+1;
while(pos[ne].size()&&*pos[ne].begin()<a[i].second)
pos[ne].erase(pos[ne].begin());
if(a[i].first!=a[i+1].first)
ans=max(ans,dp[i]+(int)pos[ne].size());
else
ans=max(ans,i-l+1+(int)pos[ne].size());
}
printf("%d\n",n-ans);
for(int i=1;i<=n;i++)
pos[i].clear(),dp[i]=0;
}
return 0;
}