Codeforces 1367 F2 Flying Sort (Hard Version) —— 两种方法

This way

题意:

给你n个数,你每次可以将这个数组里的一个数放到最前面或者放到最后面,问你最后要使得这个数组变成升序,问你最少需要操作几次。

题解:

我没想到一个很关键的点,于是就一直想不出来,先介绍一下看来的一个大神的操作
首先将这个数组离散化,然后可以知道,我们选取的不动序列应该是这样的:
假设我们取得值域是x-y,那么值在x-y之间的所有数都应该取并且他们一定是已经有序了的,值是x-1的数的序列应当取在最小下标的x之前的一个前缀,值是y+1的数的序列应当取在最大下标的y之后的一个后缀。
在这里插入图片描述

操作1

那么这个大神的操作是这样的,以{value,index}\{value,-index\}的关键字排序之后,这个数组是先以值从小到大排序,值相同以下标从大到小排序。
然后从小到大去做,假设当前的数的值是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;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章