• 「USACO18OPEN」Out of Sorts P


    题目

    点这里看题目。

    分析

    容易看出,将序列分割并且递归,其实并不会影响排序过程。它影响的是 work_counter 的值——如果分割出了长度为 1 的序列,那么这个序列里面的值将不再贡献。而分割出长度为 1 的序列,其实就相当于某个元素到了它应该到的位置,并且在此之后不会被挪动。我们称这个元素成为了一个“分割点”。

    不妨先考虑 (a) 全部不相等的情况,这时可以放心地对序列进行离散化。那么对于一个元素单独考虑,经过了若干轮冒泡之后,如果没有以它结尾的逆序对,同时它到了该到的地方,那么它就不会继续贡献了。注意这个条件按冒泡轮数具有单调性,因此我们可以二分它什么时候成为“分割点”。

    此时我们需要解决的问题是,求出若干轮冒泡排序后,某个元素的位置。这是经典问题,设 (b_i) 为由 (i) 结尾的逆序对个数,则若排序轮数 (xle b_i),元素 (i) 只会向前平移 (x) 个位置。对于 (x>b_i) 的元素,它们已经“松动”,会从大到小、从后往前依次填入还没有被占据的位置。

    因此,当我们对于元素 (i),检验第 (t) 轮排序结束后是否成为“分割点”时,一定会有 (tge b_i)。当 (t=b_i) 时直接检查,而当 (t>b_i) 的时候,需要算出此时它在“松动”的元素中的排名,再从此时的空位中找出它的位置,这两个操作都可以用主席树维护,因此可以 (O(log n)) 检查。这样我们得到了 (O(nlog^2n)) 的算法。

    如果 (a) 中有重复元素,我们同样可以离散化,只不过同样大小的元素离散化后从前往后应该是递增的。这样做是合理的,因为冒泡排序是稳定的排序算法,相同大小的元素不会交换前后位置。


    当然,我们还有更优的算法。对于元素 (a_x),我们可以直接根据序列的情况讨论出它经过几轮后会变成“分割点”:

    1. (a_x) 大的数,这些数必须在 (a_x) 之前才会影响到它。这些数总共会导致 (a_x) 需要等待 (b_x) 轮才会松动。换句话说,如果 ([1,x])(x) 是第 (k) 大,那么它至少需要 (k-1) 轮冒泡排序。
    2. (a_x) 小的数,这些数必须在 (a_x) 之后才会影响到它。对于任何一个 (x<y,a_x>a_y),如果 ([1,y])(x) 是第 (k) 大,则它至少需要 (k) 轮才能越过 (y)。显然对于固定的 (x),我们只需要考虑最靠右的 (y) 带来的影响。

    可以发现,两个情况取 (max) 就是 (a_x) 最终成为“分割点”的时间;并且第一种情况实际上是第二种情况中不存在 (y) 的特例,因此两种情况可以合并起来写。

    用树状数组实现即可。复杂度为 (O(nlog n))

    小结:

    1. 注意一下对于排序算法的稳定性的运用,这个平时用得比较少;
    2. 多练习一下对于问题的分析,而不要总是尝试去“算”,去“求”,太“暴力”了吧

    代码

    #include <cstdio>
    #include <vector>
    #include <utility>
    #include <assert.h>
    #include <iostream>
    #include <algorithm>
    
    #define rep( i, a, b ) for( int i = (a) ; i <= (b) ; i ++ )
    #define per( i, a, b ) for( int i = (a) ; i >= (b) ; i -- )
    
    typedef long long LL;
    
    const int MAXN = 1e5 + 5, MAXS = 2e6 + 5;
    
    template<typename _T>
    void read( _T &x )
    {
    	x = 0; char s = getchar(); int f = 1;
    	while( ! ( '0' <= s && s <= '9' ) ) { f = 1; if( s == '-' ) f = -1; s = getchar(); }
    	while( '0' <= s && s <= '9' ) { x = ( x << 3 ) + ( x << 1 ) + ( s - '0' ), s = getchar(); }
    	x *= f;
    }
    
    template<typename _T>
    void write( _T x )
    {
    	if( x < 0 ) putchar( '-' ), x = -x;
    	if( 9 < x ) write( x / 10 );
    	putchar( x % 10 + '0' );
    }
    
    std :: vector<int> num[MAXN];
    
    int BIT[MAXN], big[MAXN];
    
    int val[MAXN], app[MAXN], tot = 0;
    
    int A[MAXN], pos[MAXN];
    int N;
    
    inline void Up( int &x ) { x += ( x & ( -x ) ); }
    inline void Down( int &x ) { x -= ( x & ( -x ) ); }
    inline void Update( int x, int v ) { for( ; x <= N ; Up( x ) ) BIT[x] += v; }
    inline int Query( int x ) { int ret = 0; for( ; x ; Down( x ) ) ret += BIT[x]; return ret; }
    
    namespace Sequence
    {
    	int emp[MAXS], lch[MAXS], rch[MAXS];
    	int rt[MAXN]; int ntot = 0;
    	
    	inline void Upt( const int x ) { emp[x] = emp[lch[x]] + emp[rch[x]]; }
    	
    	inline void Copy( const int a, const int b )
    	{
    		emp[a] = emp[b], lch[a] = lch[b], rch[a] = rch[b];
    	}
    	
    	int Update( const int x, const int l, const int r, const int p )
    	{
    		int cur = ++ ntot; Copy( cur, x );
    		if( l == r ) { emp[cur] ++; return cur; }
    		int mid = ( l + r ) >> 1;
    		if( p <= mid ) lch[cur] = Update( lch[x], l, mid, p );
    		else rch[cur] = Update( rch[x], mid + 1, r, p );
    		Upt( cur ); return cur;
    	}
    	
    	int Query( const int x, const int l, const int r, const int rnk )
    	{
    		if( l == r ) return l;
    		int mid = ( l + r ) >> 1;
    		if( rnk <= emp[rch[x]] ) return Query( rch[x], mid + 1, r, rnk );
    		return Query( lch[x], l, mid, rnk - emp[rch[x]] );
    	}
    
    	void Build()
    	{
    		rep( i, 0, N - 1 )
    		{
    			rt[i] = rt[i - 1];
    			rep( j, 0, ( int ) num[i].size() - 1 )
    				rt[i] = Update( rt[i], 1, N, num[i][j] );
    		}
    	}
    
    	int Query( const int t, const int x ) { return Query( rt[t - 1], 1, N, x ); }
    }
    
    namespace Number
    {
    	int cnt[MAXS], lch[MAXS], rch[MAXS];
    	int rt[MAXN]; int ntot = 0;
    	
    	inline void Upt( const int x ) { cnt[x] = cnt[lch[x]] + cnt[rch[x]]; }
    	
    	inline void Copy( const int a, const int b )
    	{
    		cnt[a] = cnt[b], lch[a] = lch[b], rch[a] = rch[b];
    	}
    
    	int Update( const int x, const int l, const int r, const int p )
    	{
    		int cur = ++ ntot; Copy( cur, x );
    		if( l == r ) { cnt[cur] ++; return cur; }
    		int mid = ( l + r ) >> 1;
    		if( p <= mid ) lch[cur] = Update( lch[x], l, mid, p );
    		else rch[cur] = Update( rch[x], mid + 1, r, p );
    		Upt( cur ); return cur;
    	}
    	
    	int Query( const int x, const int l, const int r, const int segL, const int segR )
    	{
    		if( ! x ) return 0;
     		if( segL <= l && r <= segR ) return cnt[x];
     		int mid = ( l + r ) >> 1, ret = 0;
     		if( segL <= mid ) ret += Query( lch[x], l, mid, segL, segR );
     		if( mid  < segR ) ret += Query( rch[x], mid + 1, r, segL, segR );
     		return ret;
    	}
    
    	void Build()
    	{
    		rep( i, 0, N - 1 )
    		{
    			rt[i] = rt[i - 1];
    			rep( j, 0, ( int ) num[i].size() - 1 )
    				rt[i] = Update( rt[i], 1, N, A[num[i][j]] );
    		}
    	}
    	
    	int Query( const int t, const int x ) { return Query( rt[t - 1], 1, N, x, N ) - t; }
    }
    
    bool Chk( const int x, const int tim )
    {
    	if( N - x + 1 <= tim ) return true;
    	if( big[x] == tim ) return pos[x] - tim == x;
    	int rnk = Number :: Query( tim, x );
    	return Sequence :: Query( tim, rnk ) - tim == x;
    }
    
    int main()
    {
    	read( N );
    	rep( i, 1, N ) read( A[i] ), val[++ tot] = A[i];
    	std :: sort( val + 1, val + 1 + tot );
    	bool flg = true;
    	rep( i, 1, N )
    	{
    		int x = std :: lower_bound( val + 1, val + 1 + tot, A[i] ) - val;
    		A[i] = x + ( app[x] ++ );
    	}
    	rep( i, 1, N ) flg &= A[i] == i;
    	rep( i, 1, N ) pos[A[i]] = i;
    	if( N == 1 ) return puts( "0" ), 0;
    	
    	rep( i, 1, N ) 
    	{
    		big[A[i]] = i - 1 - Query( A[i] );
    		Update( A[i], 1 );
    		num[big[A[i]]].push_back( i );
    	}
    	
    	Sequence :: Build();
    	Number :: Build();
    	
    	LL ans = 0;
    	rep( i, 1, N )
    	{
    		int l = big[i], r = N - 1, mid;
    		while( l < r )
    		{
    			mid = ( l + r ) >> 1;
    			if( Chk( i, mid ) ) r = mid;
    			else l = mid + 1;
    		}
    		ans += std :: max( 1, l );
    	}
    	write( ans ), putchar( '
    ' );
    	return 0;
    }
    
  • 相关阅读:
    【转】winrar命令行详解
    【转】关于色彩空间sRGB和Adobe RGB...
    深入解读TPC-C指标
    解决因 RsFX devicer 而无法卸载 SQL Server 的问题
    LUHN算法
    信用卡卡号编排的含义
    关于第三方API调用的工具 WebApiClient.JIT 的使用记录
    ocelot.json模板
    C#进阶之事件与委托(Event&Delegate)
    C#进阶之面向对象(OOP)
  • 原文地址:https://www.cnblogs.com/crashed/p/15194071.html
Copyright © 2020-2023  润新知