Live2D

Solution -「洛谷 P4372」Out of Sorts P

\(\mathcal{Description}\)

  OurOJ & 洛谷 P4372(几乎一致)

  设计一个排序算法,设现在对 \(\{a_n\}\)\([l,r]\) 内的元素排序,则重复冒泡排序零次或多次,直到存在某个位置 \(p\in[l,r)\),满足 \(\max_{i=l}^p\{a_i\}<\min_{i=p+1}^r\{a_i\}\),则递归入 \([l,p]\)\((p,r]\),直到区间长度为 \(1\) 时停止。求所有冒泡排序所操作的区间长度之和。

  \(n\le10^5\),保证 \(\{a_n\}\) 无重复数字。

\(\mathcal{Solution}\)

  可以发现“递归入两个区间”是对冒泡排序一个并没有什么用的剪枝——两个区间之间一定不会出现元素交换。那么这个剪枝完全可以忽略,算法等价于不停对 \([1,n]\) 冒泡直到序列有序,唯一的区别仅有代价不同。但好处在于,以忽略递归的排序算法为基础,容易求出每个位置什么时候成为分割点 \(p\)——即不停对 \([1,n]\) 冒泡,什么时候 \(\forall a_j\in[1,i],~j\in[1,i]\):这个值就是离 \(i\) 最远的满足 \(a_j\in[1,i]\)\(j\)\(i\) 的距离,倒着扫一遍 BIT 维护即可。

  求出每个位置成为分隔点的最早时间 \(t_i\),接下来的做法包括但不限于:

  • 按题意模拟!启发式分裂模拟排序算法,\(\mathcal O(n\log n)\)
  • 单调栈!扫一遍就行,\(\mathcal O(n)\)
  • 算每个点的贡献!排序 \(\max\{t_{i-1},t_{i+1}\}\) 次之后,\(i\) 才会变成长度为 \(1\) 的区间,这就是对答案的贡献,\(\mathcal O(n)\)

\(\mathcal{Code}\)

  按题意模拟好啊,脑细胞多精贵啊。(

  注意洛谷原题中,算法会先冒泡一次再检查分割点,细节需要改改。

/*~Rainybunny~*/

#include <bits/stdc++.h>

#define rep( i, l, r ) for ( int i = l, rep##i = r; i <= rep##i; ++i )
#define per( i, r, l ) for ( int i = r, per##i = l; i >= per##i; --i )

inline char fgc() {
    static char buf[1 << 17], *p = buf, *q = buf;
    return p == q && ( q = buf + fread( p = buf, 1, 1 << 17, stdin ), p == q )
      ? EOF : *p++;
}

template<typename Tp = int>
inline Tp rint() {
    Tp x = 0; int f = 1; char s = fgc();
    for ( ; s < '0' || '9' < s; s = fgc() ) f = s == '-' ? -f : f;
    for ( ; '0' <= s && s <= '9'; s = fgc() ) x = x * 10 + ( s ^ '0' );
    return x * f;
}

template<typename Tp>
inline void wint( Tp x ) {
    if ( x < 0 ) putchar( '-' ), x = -x;
    if ( 9 < x ) wint( x / 10 );
    putchar( x % 10 ^ '0' );
}

inline void chkmax( int& a, const int b ) { a < b && ( a = b ); }
inline int imin( const int a, const int b ) { return a < b ? a : b; }

const int MAXN = 1e5, MAXLG = 16;
int n, a[MAXN + 5], dc[MAXN + 5], st[MAXN + 5][MAXLG + 5], bitw[MAXN + 5];
long long ans;

struct BIT {
    int val[MAXN + 5];
    inline void upd( int x, const int v ) {
        for ( ; x <= n; x += x & -x ) chkmax( val[x], v );
    }
    inline int ask( int x ) {
        int ret = 0;
        for ( ; x; x -= x & -x ) chkmax( ret, val[x] );
        return ret;
    }
} bit;

inline int qmin( const int l, const int r ) {
    int k = bitw[r - l + 1];
    return imin( st[l][k], st[r - ( 1 << k ) + 1][k] );
}

inline void solve( const int l, const int r, const int las ) {
    if ( l == r ) return ;
    int firc = qmin( l, r - 1 ), p = 0;
    for ( int len = 0; /* solution always exists */; ++len ) {
        if ( st[l + len][0] == firc ) { p = l + len; break; }
        if ( st[r - 1 - len][0] == firc ) { p = r - len - 1; break; }
    }
    ans += ( r - l + 1ll ) * ( firc - las );
    solve( l, p, firc ), solve( p + 1, r, firc );
}

int main() {
    freopen( "sort.in", "r", stdin );
    freopen( "sort.out", "w", stdout );

    n = rint();
    rep ( i, 1, n ) a[i] = dc[i] = rint();
    std::sort( dc + 1, dc + n + 1 ); // no need to unique.
    // assert( std::unique( dc + 1, dc + n + 1 ) - dc - 1 == n );
    rep ( i, 1, n ) a[i] = std::lower_bound( dc + 1, dc + n + 1, a[i] ) - dc;

    bit.upd( a[n], n );
    per ( i, n - 1, 1 ) {
        if ( ( st[i][0] = bit.ask( i ) ) ) st[i][0] -= i;
        bit.upd( a[i], i );
    }

    rep ( i, 2, n ) bitw[i] = bitw[i >> 1] + 1;
    for ( int j = 1; 1 << j <= n; ++j ) {
        rep ( i, 1, n - ( 1 << j ) + 1 ) {
            st[i][j] = imin( st[i][j - 1], st[i + ( 1 << j >> 1 )][j - 1] );
        }
    }

    solve( 1, n, 0 );
    wint( ans ), putchar( '\n' );
    return 0;
}

posted @ 2021-08-27 14:56  Rainybunny  阅读(84)  评论(1编辑  收藏  举报