「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 次左右就好了。
小结:
- 依据题目的奇偶性质确定策略,也包括改变顺序的技巧。
- 随机化!!避免了糟糕情况的出现( 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;
}