题意:给你长度为n的数组,定义已经排列过的串为:相邻两项a[i],a[i+1],满足a[i]<=a[i+1]。我们每次对当前数组删除非排序过的串,合并剩下的串,继续删,直到排序完成。

题解:用双向链表模拟删除过程即可。

具体细节见代码:

#include<cstdio>
#include<cstring>
#define N 200010
using namespace std;
int T,n,i,a[N],out[N],next[N],pre[N],r,pos[N],l;
int main(){
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        l=r=0;
        memset(pos,0,sizeof(pos));
        memset(out,0,sizeof(out));
        memset(next,0,sizeof(next));
        memset(pre,0,sizeof(pre));
        for(i=1;i<=n;i++){
            scanf("%d",&a[i]);
            next[i]=i+1;
            pre[i]=i-1;
            if(a[i]<a[i-1]){///标记第一次遍历所删除的数
                if(!out[i-1])pos[++r]=i-1,out[i-1]=1;///r表示所删除的数的数量
                pos[++r]=i;out[i]=1;
            }
        }
        for(l=1;l<=r;){
            int rr=r;
            for(i=l;i<=rr;i++){
                next[pre[pos[i]]]=next[pos[i]];
                pre[next[pos[i]]]=pre[pos[i]];///将pos[i]所表示的数删去
                if(pre[pos[i]]==0||next[pos[i]]==n+1)continue;///遍历完毕
                if(!out[next[pos[i]]]&&a[next[pos[i]]]<a[pre[pos[i]]]){///如果已经删除的数后面一个数并未被删除,且这一个数小于已经删除的数的前一个数,那么则删除它
                    if(!out[pre[pos[i]]])pos[++r]=pre[pos[i]],out[pre[pos[i]]]=1;
                    pos[++r]=next[pos[i]];out[next[pos[i]]]=1;
                }
            }
            l=rr+1;
        }
        printf("%d\n",n-r);
        for(i=1;i<=n;i++)
            if(!out[i])printf("%d ",a[i]);puts("");
    }
}