codeforces 558E E. A Simple Task( 线段树+统计排序)

题目连接:

codeforces 558E


题目大意:

给出一个字母的序列(只包含小写字母),每次对它的一个区间进行排序,问最后的字母序列。


题目分析:

首先因为字母的数量很小,我们很容易想到统计统计排序这个O(26*log(n))的排序方法,对于每个要排序的区间,我们只需要从小到大统计每个字母有多少个,然后顺序输出即可,利用26棵线段树维护,可以做到O(logn)的复杂度。最后输出即可,总的复杂度O(26*n*logn)


AC代码:

 #include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#define MAX 100007

using namespace std;

int n,q;
char s[MAX];
struct Tree
{
    int sum,l,r,lazy;
};

struct SegmentTree
{
    Tree tree[MAX<<2];

    void push_up ( int u )
    {
        tree[u].sum = tree[u<<1].sum + tree[u<<1|1].sum;
    }

    void push_down ( int u )
    {
        int lazy = tree[u].lazy;
        if ( lazy != -1 )
        {
            tree[u<<1].lazy = tree[u<<1|1].lazy = lazy;
            tree[u<<1].sum = (tree[u<<1].r-tree[u<<1].l+1)*lazy;
            tree[u<<1|1].sum = (tree[u<<1|1].r-tree[u<<1|1].l+1)*lazy;
            tree[u].lazy = -1;
        }
    }

    void build ( int u , int l , int r , int a )
    {
        tree[u].l = l;
        tree[u].r = r;
        tree[u].lazy = -1;
        if ( l == r )
        {
            if ( s[l]-'a' == a ) tree[u].sum = 1;
            else tree[u].sum = 0;
            return;
        }
        int mid = l+r>>1;
        build ( u<<1 , l , mid , a );
        build ( u<<1|1 , mid+1 , r , a );
        push_up ( u );
    }

    void update ( int u , int left , int right , int v )
    {
        int l = tree[u].l;
        int r = tree[u].r;
        int mid = l+r>>1;
        if ( left <= l && r <= right )
        {
            tree[u].sum = (r-l+1)*v;
            tree[u].lazy = v;
            return;
        }
        push_down ( u );
        if ( left <= mid && right >= l )
            update ( u<<1 , left , right , v );
        if ( left <= r && right > mid )
            update ( u<<1|1 , left , right , v );
        push_up ( u );
    }

    int query ( int u , int left , int right )
    {
        int l = tree[u].l;
        int r = tree[u].r;
        int mid = l+r>>1;
        if ( left <= l && r <= right )
            return tree[u].sum;
        push_down ( u );
        int ret = 0;
        if ( left <= mid && right >= l )
            ret += query ( u<<1 , left , right );
        if ( left <= r && right > mid )
            ret += query ( u<<1|1 , left , right );
        return ret;
    }
}segmentTree[26];


inline void print ( )
{
     for ( int i = 1 ; i <= n ; i++ )
            for ( int j = 0 ; j < 26 ; j++ )
                if ( segmentTree[j].query ( 1 , i , i ) )
                {
                    putchar ( (char)(j+'a') );
                    break;
                }
        puts ( "" );
}


int main ( )
{
    int x,y,z;
    int num[26];
    while ( ~scanf ( "%d%d" , &n , &q ) )
    {
        scanf ("%s" , s+1 );
        for ( int i = 0 ; i < 26 ; i++ )
            segmentTree[i].build ( 1 , 1 , n , i );
        while ( q-- )
        {
            //print ();
            scanf ( "%d%d%d" , &x , &y , &z );
            memset ( num , 0 , sizeof ( num ));
            for ( int i = 0 ; i < 26 ; i++ )
            {
                num[i] = segmentTree[i].query( 1 , x , y );
                segmentTree[i].update ( 1 , x , y , 0 );
            }
            int l = x;
            if ( z )
            {
                for ( int i = 0 ; i < 26 ; i++ )
                {
                    segmentTree[i].update ( 1 , l , l+num[i]-1 , 1 );
                    l = l+num[i];
                }
            }
            else
            {
                for ( int i = 25 ; i >= 0 ; i-- )
                {
                    segmentTree[i].update ( 1 , l , l+num[i]-1 , 1 );
                    l = l+num[i];
                }
            }
        }
        print ();
        /*for ( int i = 1 ; i <= n ; i++ )
            for ( int j = 0 ; j < 26 ; j++ )
                if ( segmentTree[j].query ( 1 , i , i ) )
                {
                    putchar ( (char)(j+'a') );
                    break;
                }
        puts ( "" );*/
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值