题意:设原数组为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 = 200010; 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 = 0; while ( x > 0 ) { 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 ) >> 1; int sum = Query( N ) - Query( mid - 1 ); //printf( "mid=%d sum=%d ", mid, sum ); if ( sum > val ) l = mid + 1; else { if ( sum == val ) { if ( !vis[mid] ) //需要特殊判断一下这个点之前是否已经放好了 { addr = mid; r = mid - 1; } else { l = mid + 1; } } else r = mid - 1; } } return; } int main() { int T; scanf( "%d", &T ); while ( T-- ) { scanf( "%d", &N ); memset( C, 0, sizeof(int)*( N + 1 ) ); memset( vis, false, sizeof(bool)*( N + 1 ) ); for ( int i = 1; i <= N; ++i ) { scanf( "%d", &pos[i] ); Add( i, 1 ); } for ( int i = N; i > 0; --i ) { int id; BiSearch( 1, N, pos[i] + 1, id ); ans[i] = id; vis[id] = true; //printf( "id = %d ", id ); Add( id, -1 ); } printf( "%d", ans[1] ); for ( int i = 2; i <= N; ++i ) printf( " %d", ans[i] ); puts(""); } return 0; }
线段树代码: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 = 200010; int pos[MAXN]; int sum[MAXN << 2]; int ans[MAXN]; int N, id; void build( int l, int r, int rt ) { sum[rt] = r - l + 1; if ( l == r ) return; int m = ( l + r ) >> 1; 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 ) >> 1; if ( po <= sum[rt << 1] ) Update( po, lson ); else Update( po - sum[rt << 1], rson ); return; } int main() { int T; scanf( "%d", &T ); while ( T-- ) { scanf( "%d", &N ); build( 1, N, 1 ); for ( int i = 1; i <= N; ++i ) scanf( "%d", &pos[i] ); for ( int i = N; i > 0; --i ) { Update( pos[i] + 1, 1, N, 1 ); ans[i] = N - id + 1; } printf( "%d", ans[1] ); for ( int i = 2; i <= N; ++i ) printf( " %d", ans[i] ); puts(""); } return 0; }