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

题意:设原数组为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;
}
View Code

线段树代码: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;
}
View Code
原文地址:https://www.cnblogs.com/GBRgbr/p/3223343.html