题意:设原数组为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\n", 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\n", id );
            Add( id, -1 );
        }
        printf( "%d", ans[1] );
        for ( int i = 2; i <= N; ++i )
            printf( " %d", ans[i] );
        puts("");
    }
    return 0;
}
View Code

相关文章: