SPOJ 227 Ordering the Soldiers 线段树 / 树状数组

时间:2022-04-16 09:56:27

题意:设原数组为a[i],pos[i]代表第 i 个位置之前有多少个数比a[i]大,求原数组a[i]。

这个题意是看了别人的题解才明白,我自己没读出来……

方法:假设我们从左往右放,因为后面的数还有可能影响前面的数的位置,所以在最后一个数放完之前,我们没法确定每个数的位置,所以我们反过来考虑,从右往左放。因为每个数前面比它大的数的个数pos[i]已知,我们可以不必关心这些数的具体数值,从而转化为它从右往左走了多少个空格,即pos[i]个,因此这个数放在第 pos[i] + 1 个空格位置上。这个空格所在位置的下标id,即是a[i]。a[i] = id;

树状数组或线段树记录区间[1, i ]的空格个数,对于放好的数,相应区间的空格数减1。

树状数组代码:5360 ms

#include <cstdio>
#include <cstring>
#include <cstdlib> const int MAXN = ; int pos[MAXN];
int C[MAXN];
int ans[MAXN];
bool vis[MAXN];
int N; int lowbit( int x )
{
return x&(-x);
} void Add( int x, int val )
{
while ( x <= N )
{
C[x] += val;
x += lowbit(x);
}
return;
} int Query( int x )
{
int res = ;
while ( x > )
{
res += C[x];
x -= lowbit(x);
}
return res;
} //注意二分查找的写法,不然很容易死循环
void BiSearch( int l, int r, int val, int &addr )
{
int mid;
while ( l <= r )
{
mid = ( l + r ) >> ;
int sum = Query( N ) - Query( mid - );
//printf( "mid=%d sum=%d\n", mid, sum );
if ( sum > val ) l = mid + ;
else
{
if ( sum == val )
{
if ( !vis[mid] ) //需要特殊判断一下这个点之前是否已经放好了
{
addr = mid;
r = mid - ;
}
else
{
l = mid + ;
}
}
else r = mid - ;
}
}
return;
} int main()
{
int T;
scanf( "%d", &T );
while ( T-- )
{
scanf( "%d", &N );
memset( C, , sizeof(int)*( N + ) );
memset( vis, false, sizeof(bool)*( N + ) );
for ( int i = ; i <= N; ++i )
{
scanf( "%d", &pos[i] );
Add( i, );
} for ( int i = N; i > ; --i )
{
int id;
BiSearch( , N, pos[i] + , id );
ans[i] = id;
vis[id] = true;
//printf( "id = %d\n", id );
Add( id, - );
}
printf( "%d", ans[] );
for ( int i = ; i <= N; ++i )
printf( " %d", ans[i] );
puts("");
}
return ;
}

线段树代码:1840 ms

#include <cstdio>
#include <cstring>
#include <cstdlib> #define lson l, m, rt << 1
#define rson m + 1, r, rt << 1 | 1 const int MAXN = ; int pos[MAXN];
int sum[MAXN << ];
int ans[MAXN];
int N, id; void build( int l, int r, int rt )
{
sum[rt] = r - l + ;
if ( l == r ) return; int m = ( l + r ) >> ;
build( lson );
build( rson );
return;
} void Update( int po, int l, int r, int rt )
{
--sum[rt];
if ( l == r )
{
id = l;
return;
} int m = ( l + r ) >> ; if ( po <= sum[rt << ] ) Update( po, lson );
else Update( po - sum[rt << ], rson ); return;
} int main()
{
int T;
scanf( "%d", &T );
while ( T-- )
{
scanf( "%d", &N );
build( , N, );
for ( int i = ; i <= N; ++i )
scanf( "%d", &pos[i] ); for ( int i = N; i > ; --i )
{
Update( pos[i] + , , N, );
ans[i] = N - id + ;
}
printf( "%d", ans[] );
for ( int i = ; i <= N; ++i )
printf( " %d", ans[i] );
puts("");
}
return ;
}