[NOIP2017模拟]新排序

来源:互联网 发布:950x120淘宝店招图 编辑:程序博客网 时间:2024/05/21 19:54

2017.10.17 T2 1990

样例数据
输入

4
5
1 2 3 4 5
5
5 4 3 2 1
5
1 2 3 2 1
5
2 4 1 3 5

输出

5
1 2 3 4 5
0

2
1 2
3
2 3 5

分析:T3的博客也说过了,哎,这道题很痛。由于每次扫一遍删除不合法序列是O(N2)的,我们要想办法优化它,想起才考的跑步一题,用双向链表将O(N2)降为O(N),十分巧妙,是不是这道题也可以用呢?答案当然是肯定的。
我们先扫一次原序列,将不合法的点打上标记(此时已经保证没打标记的部分是一段段单调递增的序列),将一段不合法的点的左端点左边的数和右端点右边的数,也就是删除不合法序列后连接在了一起的点,加入队列(因为删掉不合法的点之后这两个会并到一起,而由于这两个新加点都存在于各自单调递增的序列中,所以它们的左边(右边)的点都还是合法的,也就不存在会和它们一起被删的情况),再对队列进行扫描,同理加入(当然要判断一下加入的点在不在序列中、是不是已经被删掉了,如果是那就不加),这样相当于就不用把整个序列都扫一遍而是找到了删点的关键,大大节省了时间。每次O(1)删除不和谐数,然后再依次检查合并或者删除子串,细节较多,详见代码。

代码

#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<string>#include<ctime>#include<cmath>#include<algorithm>#include<cctype>#include<iomanip>#include<queue>#include<set>using namespace std;int getint(){    int sum=0,f=1;    char ch;    for(ch=getchar();!isdigit(ch)&&ch!='-';ch=getchar());    if(ch=='-')    {        f=-1;        ch=getchar();    }    for(;isdigit(ch);ch=getchar())        sum=(sum<<3)+(sum<<1)+ch-48;    return sum*f;}const int N=1e5+10;const int INF=0x3f3f3f3f;int T,a[N],b[N],n,cnt,tot,p,ans;bool exist[N],del[N],bj;struct node{    int pos;    int val;}que[N];int main(){    freopen("sort.in","r",stdin);    freopen("sort.out","w",stdout);    T=getint();    while(T--)    {        memset(del,false,sizeof(del));//清零操作        memset(exist,false,sizeof(exist));        bj=false;cnt=0;        n=getint();        for(int i=1;i<=n;++i)            a[i]=getint();        a[0]=-INF,a[n+1]=INF;//要让a[1]、a[n]也能比较,就加两个边界        for(int i=1;i<=n;++i)            if(a[i]>a[i+1]||a[i]<a[i-1])//第一遍扫,找不合法(所有的单调递减序列都被剔除)            {                del[i]=true;                bj=true;            }        for(int i=1;i<=n;++i)        {            if(del[i]==true)//添加删除后挨在一起的点到队列中            {                if(i>1&&del[i-1]==false&&exist[i-1]==false)                    que[++cnt].pos=i-1,que[cnt].val=a[i-1],exist[i-1]=true;                if(i<n&&del[i+1]==false&&exist[i+1]==false)                    que[++cnt].pos=i+1,que[cnt].val=a[i+1],exist[i+1]=true;            }        }        while(bj==true)//只要还有不合法的点就要继续删        {            que[0].val=-INF,que[cnt+1].val=INF;//清零操作            bj=false,tot=0;            for(int i=1;i<=cnt;++i)            {                if(que[i].val>que[i+1].val||que[i].val<que[i-1].val)//不合法                {                    del[que[i].pos]=true;//删除标记                    exist[que[i].pos]=false;//出队列标记                    bj=true;                }            }            for(int i=1;i<=cnt;++i)            {                p=que[i].pos;                if(del[p])//同上加入点                {                    if(p>1&&del[p-1]==false&&exist[p-1]==false)                        que[++tot].pos=p-1,que[tot].val=a[p-1],exist[p-1]=true;                    if(p<n&&del[p+1]==false&&exist[p+1]==false)                        que[++tot].pos=p+1,que[tot].val=a[p+1],exist[p+1]=true;                }                else//如果还合法就留着                    que[++tot]=que[i];            }            cnt=tot;//队列长度更新        }        ans=0;        for(int i=1;i<=n;++i)            if(del[i]==false) ans++,b[ans]=a[i];        cout<<ans<<'\n';        for(int i=1;i<=ans;++i)            cout<<b[i]<<" ";        cout<<'\n';    }    return 0;}

本题结。