5016. 数列

题目大意

给定一个长度为 n 的数列,依次将数列写在黑板上,每次可以将当前这个数写在已有数列的前面或者后面。求最终得到的数列的最长上升子序列是多长,有多少种方案可以得到这个长度的最长上升子序列。

Data Constraint
n200000

题解

对于一个位置 i ,它最终所能构成的最长上升子序列必定是由以它为起始位置的最长上升子序列和以它为起始位置的最长下降子序列拼接而成,分别设长度为len1,len2。那么当前可以贡献的长度是 len1+len21 ,其余的 n(len1+len21) 个数都可以任放。所以用树状数组维护即可。

时间复杂度: O(nlogn)

SRC

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std ;

#define N 200000 + 10
typedef long long ll ;
const int MO = 1e9 + 7 ;
struct Note {
    int v , h ;
} tp[N] ;
struct Stype {
    int v , tot ;
    Stype ( int X = 0 , int Y = 0 ) { v = X , tot = Y ; }
} T[2][N] ;

int A[N] , Tab[N] ;
int n , Cnt , ans1 , ans2 ;

bool cmp( Note a , Note b ) { return a.v < b.v ; }

Stype Find( int type , int x ) {
    Stype ret ;
    ret.v = 0 , ret.tot = 1 ;
    while ( x ) {
        if ( T[type][x].v > ret.v ) {
            ret = T[type][x] ;
        } else if ( T[type][x].v == ret.v ) ret.tot = (ret.tot + T[type][x].tot) % MO ;
        x -= x & (-x) ;
    }
    return ret ;
}

void Insert( int type , int x , Stype val ) {
    while ( x <= Cnt ) {
        if ( val.v > T[type][x].v ) {
            T[type][x].v = val.v ;
            T[type][x].tot = val.tot ;
        } else if ( val.v == T[type][x].v ) {
            T[type][x].tot = (T[type][x].tot + val.tot) % MO ;
        }
        x += x & (-x) ;
    }
}

int main() {
    freopen( "sequence.in" , "r" , stdin ) ;
    freopen( "sequence.out" , "w" , stdout ) ;
    scanf( "%d" , &n ) ;
    Tab[0] = 1 ;
    for (int i = 1 ; i <= n ; i ++ ) Tab[i] = Tab[i-1] * 2 % MO ;
    for (int i = 1 ; i <= n ; i ++ ) {
        scanf( "%d" , &A[i] ) ;
        tp[i].v = A[i] , tp[i].h = i ;
    }
    sort( tp + 1 , tp + n + 1 , cmp ) ;
    tp[0].v = -1 ;
    for (int i = 1 ; i <= n ; i ++ ) {
        if ( tp[i].v != tp[i-1].v ) ++ Cnt ;
        A[tp[i].h] = Cnt ;
    }
    Cnt ++ ;
    for (int i = n ; i >= 1 ; i -- ) {
        Stype ret0 = Find( 0 , Cnt - (A[i] + 1) ) ;
        Stype ret1 = Find( 1 , A[i] - 1 ) ;
        int len = ret0.v + ret1.v + 1 ;
        if ( len > ans1 ) {
            ans1 = len ;
            ans2 = ((ll)ret0.tot * ret1.tot % MO * Tab[n-len]) % MO ;
        } else if ( len == ans1 ) {
            ans2 = (ans2 + ((ll)ret0.tot * ret1.tot % MO * Tab[n-len]) % MO) % MO ;
        }
        Insert( 0 , Cnt - A[i] , Stype( ret0.v + 1 , ret0.tot ) ) ;
        Insert( 1 , A[i] , Stype( ret1.v + 1 , ret1.tot ) ) ;
    }
    printf( "%d %d\n" , ans1 , ans2 ) ;
    return 0 ;
}

以上.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值