「ROI 2018 Day 2」快速排序

题目

点这里看题目。

分析

从来不会做构造题.jpg

考虑操作次数 \(O(n\log_2n)\) 的方法。

我们可以从前往后枚举第 \(i\) 位,然后在当前排列中找到 \(i\) 的位置。此时我们就需要将数 \(i\) 移动到第 \(i\) 位上。普通的做法是使用冒泡排序交换相邻位;更加巧妙的方法是,依靠题目性质,选定一个区间使得 \(i\) 恰好位于区间内的奇数位上,那么我们就可以令 \(i\) 的下标(将近)折半。假设当前数 \(i\) 位于 \(p\) ,我们只需要考虑 \([p,i]\) 或者 \((p,i]\) 两种情况。

这样就可以得到 80 pts 的好成绩。

考虑优化:

  • 考虑倒着交换,也就是将区间分裂并奇偶合并。

    此时我们就需要从 \(1\sim n\) 的排列变为给定的排列。策略也就变成了倒着枚举 \(i\) ,并且在选取区间长度的时候要保证 \(i\) 位于区间的前半段

    注意到正着交换和倒着交换的花费次数并不相同,(来自官方题解)的表格很好地展示了这一点:

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
    4 3 3 2 2 2 2 1 1 1 1 1 1 1 1 0
    4 4 4 4 4 4 4 4 3 3 3 3 2 2 1 0

    第一行给出了倒着操作时各位到 16 的最小步数,第二行给出了正着操作时 16 到各位的最小步数。

    可以发现倒着操作的常数远小于正着操作。

  • 为了避免出题人构造数据使得步数爆炸,我们需要对初始序列进行随机化。随便操作 100 次左右就好了。

小结:

  1. 依据题目的奇偶性质确定策略,也包括改变顺序的技巧。
  2. 随机化!!避免了糟糕情况的出现( Sherwood )。

代码

#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm> 
using namespace std;

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

const int INF = 0x3f3f3f3f;
const int MAXN = 3e5 + 5;

template<typename _T>
void read( _T &x )
{
	x = 0;char s = getchar();int f = 1;
	while( s > '9' || s < '0' ){if( s == '-' ) f = -1; s = getchar();}
	while( s >= '0' && 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 ) + 1; }
	if( 9 < x ){ write( x / 10 ); }
	putchar( x % 10 + '0' );
}

template<typename _T>
_T MAX( const _T a, const _T b )
{
	return a > b ? a : b;
}

int mx[MAXN << 2], tag[MAXN << 2];

vector<int> same[MAXN];
int id[MAXN], nxt[MAXN], rnk[MAXN], len[MAXN];
int tot;

int fir[MAXN];
char S[MAXN];
int N, mxl;

bool ans[MAXN];

void Add( int x, int v ) { mx[x] += v, tag[x] += v; }
void Upt( const int x ) { mx[x] = MAX( mx[x << 1], mx[x << 1 | 1] ); }
bool Cmp( const int &x, const int &y ) { return rnk[nxt[x]] < rnk[nxt[y]]; }
void Normalize( int x ) { if( tag[x] ) Add( x << 1, tag[x] ), Add( x << 1 | 1, tag[x] ), tag[x] = 0; }

void Build( const int x, const int l, const int r )
{
	if( l > r ) return ; mx[x] = - INF;
	if( l == r ) return void( mx[x] += len[id[l]] );
	int mid = l + r >> 1;
	Build( x << 1, l, mid );
	Build( x << 1 | 1, mid + 1, r );
	Upt( x );
}

void Update( const int x, const int l, const int r, const int segL, const int segR, const int delt )
{
	if( segL > segR ) return ;
	if( segL <= l && r <= segR ) return void( Add( x, delt ) );
	int mid = l + r >> 1; Normalize( x );
	if( segL <= mid ) Update( x << 1, l, mid, segL, segR, delt );
	if( mid < segR ) Update( x << 1 | 1, mid + 1, r, segL, segR, delt );
	Upt( x );
}

void Query( const int x, const int l, const int r, const int lim, vector<int> &ret )
{
	if( mx[x] < 0 ) return ;
	if( l == r ) return void( ret.push_back( id[l] ) );
	int mid = l + r >> 1; Normalize( x );
	Query( x << 1, l, mid, lim, ret );
	if( mx[x << 1] < lim ) Query( x << 1 | 1, mid + 1, r, lim, ret );
}

void Change( int p, const int k ) 
{
	if( ! p ) return ; p = rnk[p];
	Update( 1, 1, tot, p, p, k * INF );
	Update( 1, 1, tot, p + 1, tot, k );
}

int Solve( const int upper )
{
	int B = mx[1];
	if( B < 0 ) return 1;
	if( B > upper ) return -1;
	vector<int> del; Query( 1, 1, tot, B, del );
	rep( i, 0, ( int ) del.size() - 1 ) Change( del[i], -1 );
	int lst = del.back();
	
	Change( nxt[lst], 1 );
	if( B <= upper && ( ~ Solve( len[lst] - 1 ) ) )
	{
		rep( i, len[lst], B ) ans[i] = true;
		return B;
	}
	Change( nxt[lst], -1 );
	if( B + 1 <= upper && ( ~ Solve( len[lst] ) ) )
	{
		rep( i, len[lst] + 1, B + 1 ) ans[i] = true;
		return B + 1;
	}
	rep( i, 0, ( int ) del.size() - 1 ) Change( del[i], 1 );
	return -1;
}

int main()
{
	freopen( "noxor.in", "r", stdin );
	freopen( "noxor.out", "w", stdout );
	read( N );
	rep( i, 1, N )
	{
		scanf( "%s", S + 1 ); 
		int l = strlen( S + 1 );
		per( j, l, 1 )
			if( S[j] == '1' )
			{
				nxt[++ tot] = fir[i];
				same[len[tot] = l - j].push_back( tot );
				fir[i] = tot;
			}
		mxl = MAX( mxl, l );
	}
	rnk[0] = tot + 1;
	int p = tot;
	rep( i, 0, mxl - 1 )
	{
		sort( same[i].begin(), same[i].end(), Cmp );
		p -= same[i].size();
		rep( j, 0, ( int ) same[i].size() - 1 )
			id[rnk[same[i][j]] = ++ p] = same[i][j];
		p -= same[i].size();
	}
	Build( 1, 1, tot );
	rep( i, 1, N ) Change( fir[i], 1 );
	per( i, Solve( N + mxl ), 0 ) putchar( ans[i] + '0' );
	puts( "" );
	return 0;
}
posted @ 2021-02-23 21:15  crashed  阅读(262)  评论(0编辑  收藏  举报