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\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

线段树代码: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

 

 

你可能感兴趣的:(order)