2019 Multi-University Training Contest 2 1002 Beauty Of Unimodal Sequence —— DP+树状数组求单峰序列最长时字典序最小&最大

202 篇文章 6 订阅

This way

题意:

给你一个长度为n的数组,让你选出一个序列使得它是这个数组中的最长单峰序列。输出这类序列字典序最小和最大的序列。

题解:

p r e [ i ] [ 0 ] pre[i][0] pre[i][0] 表示 a [ i ] a[i] a[i] 一定取,序列 a [ 1.. i ] a[1..i] a[1..i] 的最长上升子序列长度。
p r e [ i ] [ 1 ] pre[i][1] pre[i][1] 表示 a [ i ] a[i] a[i] 一定取,序列 a [ 1.. i ] a[1..i] a[1..i] 的最长单峰子序列长度。
s u f [ i ] [ 0 ] suf[i][0] suf[i][0] 表示 a [ i ] a[i] a[i] 一定取,序列 a [ i . . n ] a[i..n] a[i..n] 的最长下降子序列长度。
s u f [ i ] [ 1 ] suf[i][1] suf[i][1] 表示 a [ i ] a[i] a[i] 一定取,序列 a [ i . . n ] a[i..n] a[i..n] 的最长单峰子序列长度。

那么状态转移的时候我们就可以用两个树状数组维护当前上升序列和当前单峰序列的最大值

至于求字典序最小和最大,由于字典序是可以贪心找的,因为合法时前一位更优那么就是答案更优。所以我们可以一位一位的考虑。如果当前的位置是在某个最长序列中,并且它是正好在我们枚举到的字典序最小|大的当前位置,也就是
p r e [ i ] [ 0 ] = = c n t + 1 pre[i][0]==cnt+1 pre[i][0]==cnt+1,那么就记入答案。或者如果它成为下降序列时也是满足的, s u f [ i ] [ 0 ] + c n t = = m x suf[i][0]+cnt==mx suf[i][0]+cnt==mx那么我们也记入答案。但是对于这种情况,我们之后所考虑的就是下降序列了。
找字典序最大也是同理。

代码有点长,但是分块看的话还是很好理解的

#include<bits/stdc++.h>
using namespace std;
const int N=3e5+5;
int a[N],b[N],pre[N][2],suf[N][2];
int lowbit(int x){return x&(-x);}
int asc[N],desc[N];
void addasc(int x,int v)
{
    for(int i=x;i<N;i+=lowbit(i))
        asc[i]=max(asc[i],v);
}
int queryasc(int x)
{
    int ans=0;
    for(int i=x;i;i-=lowbit(i))
        ans=max(ans,asc[i]);
    return ans;
}
void adddesc(int x,int v)
{
    for(int i=x;i;i-=lowbit(i))
        desc[i]=max(desc[i],v);
}
int querydesc(int x)
{
    int ans=0;
    for(int i=x;i<N;i+=lowbit(i))
        ans=max(ans,desc[i]);
    return ans;
}
struct node
{
    int id,p;
    bool operator< (const node& aa)const
    {
        if(p!=aa.p)
            return p<aa.p;
        return id<aa.id;
    }
};
vector<node>vec;
int num[N],ans[N][2];
int main()
{
    int n;
    while(~scanf("%d",&n))
    {
        memset(asc,0,sizeof(asc));
        memset(desc,0,sizeof(desc));
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]),b[i]=a[i];
            for(int j=0;j<=1;j++)
                pre[i][j]=suf[i][j]=ans[i][j]=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]=lower_bound(b+1,b+1+all,a[i])-b;
        for(int i=1;i<=n;i++)
        {
            int k=queryasc(a[i]-1);
            pre[i][0]=k+1;
            k=querydesc(a[i]+1);
            pre[i][1]=k+1;
            addasc(a[i],pre[i][0]),adddesc(a[i],max(pre[i][1],pre[i][0]));
        }
        memset(asc,0,sizeof(asc));
        memset(desc,0,sizeof(desc));
        int mx=0;
        for(int i=n;i>=1;i--)
        {
            int k=queryasc(a[i]-1);
            suf[i][0]=k+1;
            k=querydesc(a[i]+1);
            suf[i][1]=k+1;
            addasc(a[i],suf[i][0]),adddesc(a[i],max(suf[i][1],suf[i][0]));

            num[i]=max(pre[i][0]+suf[i][0]-1,max(pre[i][1]+suf[i][0]-1,pre[i][0]+suf[i][1]-1));
            mx=max(num[i],mx);
        }
        int f=0;
        int cnt=0;
        for(int i=1;i<=n;i++)
        {
            if(num[i]==mx)
            {
                if(f)
                {
                    if(cnt+suf[i][0]==mx)
                        ans[++cnt][0]=i;
                }
                else
                {
                    if(pre[i][0]==cnt+1)
                        ans[++cnt][0]=i;
                    else if(suf[i][0]+cnt==mx)
                        ans[++cnt][0]=i,f=1;
                }
            }
        }
        cnt=f=0;
        for(int i=n;i>=1;i--)
        {
            if(num[i]==mx)
            {
                if(f)
                {
                    if(cnt+pre[i][0]==mx)
                        ans[mx-cnt][1]=i,cnt++;
                }
                else
                {
                    if(cnt+1==suf[i][0])
                        ans[mx-cnt][1]=i,cnt++;
                    else if(pre[i][0]+cnt==mx)
                        ans[mx-cnt][1]=i,cnt++;
                }
            }
        }
        for(int i=1;i<=mx;i++)
            printf("%d%c",ans[i][0],i==mx?'\n':' ');
        for(int i=1;i<=mx;i++)
            printf("%d%c",ans[i][1],i==mx?'\n':' ');
    }
    return 0;
}
/*
10
10 4 5 8 4 6 8 5 3 10

*/

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值