HDU6592&杭电多校第二场B

单调栈解法

#include<iostream>
#include<cstdio>
#include<cstring>
#include<stack>
using namespace std;

const int maxn=3e5+4;
int pos1[maxn],pos2[maxn],d[maxn];  //pos1[i]表示以i位置结尾的LIS长度,d[i]表示长度为i的LIS结尾元素最小值
int ans[maxn],a[maxn],p[maxn];
int n,tot;

int main(){
    while(~scanf("%d",&n)){
        memset(d,0x3f,sizeof(d));
        d[0]=0;
        for(int i=1;i<=n;i++){
            scanf("%d",a+i);
            pos1[i]=lower_bound(d+1,d+1+n,a[i])-d;
            d[pos1[i]]=a[i];
        }
        memset(d,0x3f,sizeof(d));
        d[0]=0;
        for(int i=n;i>0;--i){
            pos2[i]=lower_bound(d+1,d+n+1,a[i])-d;
            d[pos2[i]]=a[i];
        }
        //计算字典序最小的情况
        //从左至右遍历找到的是第一个最长串的峰
        //从这个峰向两侧找组成LIS的顶点即可,向左需要用单调栈尽可能向前找(遇到同样满足条件的,栈会弹出抛弃原有的),向右只需贪心遍历(有满足条件的就要了)即可
        int mx=pos1[1]+pos2[1];
        int now=1;
        for(int i=2;i<=n;i++){
            if(pos1[i]+pos2[i]>mx){
                mx=pos1[i]+pos2[i];
                now=i;
            }
        }
        stack<int> s;
        int p[maxn];    //p[i]表示长度为i的LIS结尾的最小值
        memset(p,0,sizeof(p));
        p[pos1[now]]=a[now];
        for(int i=now-1;i>0;i--){
            if(a[i]>=p[pos1[i]+1])
                continue;
            while(!s.empty()&&a[i]>=a[s.top()])   //关于单调性的选择,最开始我的想法是直接比较元素大小,不过不如题解中的比较pos直观,经实测比较pos或a均可
                s.pop();
            s.push(i);
            p[pos1[i]]=a[i];
        }
        tot=0;
        while(!s.empty()){
            ans[++tot]=s.top();
            s.pop();
        }
        ans[++tot]=now;
        int last=now;
        for(int i=now+1;i<=n;i++){
            if(pos2[i]==pos2[last]-1&&a[i]<a[last]){
                ans[++tot]=i;
                last=i;
            }
        }
        for(int i=1;i<=tot;i++){
            printf("%d%c",ans[i],(i==tot?'\n':' '));
        }

        //计算字典序最大的情况,对称
        //左边的从峰贪心地向左匹配即可,右边的需单调栈
        mx=pos1[1]+pos2[1];
        now=1;
        for(int i=2;i<=n;++i){
            if(pos1[i]+pos2[i]>=mx){
                mx=pos1[i]+pos2[i];
                now=i;
            }
        }
        memset(p,0,sizeof(p));  //设为零,这样在检验时,如果pos值小2或更多,将会和0值比较,这个点本身也是不能采用的。
        tot=0;
        last=now;
        for(int i=now-1;i>0;i--){
            if(pos1[i]==pos1[last]-1&&a[i]<a[last]){
                ans[pos1[now]-(++tot)]=i;
                last=i;
            }
        }
        ans[++tot]=now;
        p[pos2[now]]=a[now];
        for(int i=now+1;i<=n;i++){
            if(a[i]>=p[pos2[i]+1])
                continue;
            while(!s.empty()&&a[i]>=a[s.top()])
                s.pop();
            p[pos2[i]]=a[i];
            s.push(i);
        }
        while(!s.empty()){
            ans[mx-(++tot)+pos1[now]]=s.top();
            s.pop();
        }
        for(int i=1;i<=tot;i++)
            printf("%d%c",ans[i],(i==tot?'\n':' '));
    }
    return 0;
}

线段树解法

待补充

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Absoler

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值