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;
}

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