• 计数排序 + 线段树优化 --- Codeforces 558E : A Simple Task


     E. A Simple Task

    Problem's Link: http://codeforces.com/problemset/problem/558/E


     

    Mean: 

    给定一个字符串,有q次操作,每次操作将(l,r)内的字符升序或降序排列,输出q次操作后的字符串。

    analyse:

    基本思想是计数排序

    所谓计数排序,是对一个元素分布较集中的数字集群进行排序的算法,时间复杂度为O(n),但使用条件很苛刻。首先对n个数扫一遍,映射出每个数字出现的次数,然后再O(n)扫一遍处理出:对于数字ai,有多少个数字在ai前面。有了这个信息,我们就可以在O(1)的时间内确定出排序后ai所在的位置。

    解题思路:

    对于每个Query,我们先统计出(l,r)区间内每个字母出现的次数,然后分类来排序(非升或非降)。这个更新操作就相当于:

    for(int j=x; j<=y; j++)
          cnt[s[j] - 'a']++;
    ind = 0;
    for(int j=x; j<=y; j++)
    {
          while(cnt[ind] == 0)
                ind++;
          s[j] = ind + 'a';
          cnt[ind]--;
    }

    但是每次这样去统计时间复杂度是O(n),对于(10^5)*(5*10^4)的时间复杂度势必超时。所以我们需要一种在区间更新和统计上时间复杂度都客观的数据结构---线段树。

    我们开26棵线段树,第i棵线段树维护的是:26个字母中排第i个的字母在各个区间的数目。

    这样一来,我们就可以将一个字符串S完美的融入到这26棵线段树中去,更新和查找都从上面的O(n)变为了O(logn)。其中传递更新需要用Lazy标记。

    Time complexity: O(q*logn*sz)

     

    Source code: 

    /*
    * this code is made by crazyacking
    * Verdict: Accepted
    * Submission Date: 2015-07-15-21.40
    * Time: 0MS
    * Memory: 137KB
    */
    #include <queue>
    #include <cstdio>
    #include <set>
    #include <string>
    #include <stack>
    #include <cmath>
    #include <climits>
    #include <map>
    #include <cstdlib>
    #include <iostream>
    #include <vector>
    #include <algorithm>
    #include <cstring>
    #define  LL long long
    #define  ULL unsigned long long
    using namespace std;
    
    #define MX  100007
    #define lft (idx<<1)
    #define rgt (lft|1)
    #define mid ((l+r)>>1)
    #define rep(i,x,y) for(int i=x;i<=y;++i)
    
    int Tree[27][4*MX];
    int Lazy[27][4*MX];
    char s[MX];
    
    void Build(int idx,int l,int r)
    {
          if(l == r)
          {
                int id = s[l]-'a'+1;
                Tree[id][idx] = 1;
                return;
          }
          Build(lft,l,mid);
          Build(rgt,mid+1,r);
          rep(i,1,26) Tree[i][idx] = Tree[i][lft] + Tree[i][rgt]; //回溯pushup
    }
    
    void Pushup(int id,int idx,int l,int r,int v)
    {
          Lazy[id][idx] = v;
          Tree[id][idx] = (r-l+1)*(v%2);
    }
    
    void Update(int id,int idx,int l,int r,int s,int e,int v)
    {
          if(l==s && r==e)
          {
                Pushup(id,idx,l,r,v);
                return;
          }
          if(Lazy[id][idx])
          {
                Pushup(id,lft,l,mid,Lazy[id][idx]);
                Pushup(id,rgt,mid+1,r,Lazy[id][idx]);
                Lazy[id][idx] = 0;
          }
          if(e <= mid) { Update(id,lft,l,mid,s,e,v); }
          else if(s > mid) { Update(id,rgt,mid+1,r,s,e,v); }
          else { Update(id,lft,l,mid,s,mid,v), Update(id,rgt,mid+1,r,mid+1,e,v); }
          Tree[id][idx] = Tree[id][lft] + Tree[id][rgt];
    }
    
    int Query(int id,int idx,int l,int r,int s,int e) //查询s~e这段上有多少个字母i
    {
          if(l == s && r == e) { return Tree[id][idx]; }
          if(Lazy[id][idx])
          {
                Pushup(id,lft,l,mid,Lazy[id][idx]);
                Pushup(id,rgt,mid+1,r,Lazy[id][idx]);
                Lazy[id][idx] = 0;
          }
          if(e <= mid) { return Query(id,lft,l,mid,s,e); }
          else if(s > mid) { return Query(id,rgt,mid+1,r,s,e); }
          else { return Query(id,lft,l,mid,s,mid) + Query(id,rgt,mid+1,r,mid+1,e); }
    }
    
    int main()
    {
          int n,m;
          scanf("%d %d",&n,&m);
          scanf("%s",s+1);
          Build(1,1,n);
          while(m--)
          {
                int s,e,k;
                scanf("%d %d %d",&s,&e,&k);
                int cnt[27] = {0};
                rep(i,1,26)
                {
                      cnt[i] = Query(i,1,1,n,s,e);
                      Update(i,1,1,n,s,e,2);
                }
                if(k)/**< non-decreasing */
                {
                      int l = s;
                      rep(i,1,26)
                      {
                            int st = l;
                            int ed = st+cnt[i]-1;
                            if(st <= ed) { Update(i,1,1,n,st,ed,1); } //将字符串的st到ed置为i
                            l = ed+1;
                      }
                }
                else/**< non-increasing */
                {
                      int l = s;
                      for(int i=26; i>=1; --i)
                      {
                            int st = l;
                            int ed = st+cnt[i]-1;
                            if(st <= ed) { Update(i,1,1,n,st,ed,1); }
                            l = ed+1;
                      }
                }
          }
          rep(i,1,n)
          {
                rep(j,1,26)
                {
                      int qq = Query(j,1,1,n,i,i);
                      if(qq)     {putchar('a'+j-1); break;}
                }
          }
          puts("");
          return 0;
    }
  • 相关阅读:
    Ubuntu安装Cassandra
    Ubuntu安装中文输入法
    CoreOS, Kubernetes, etcd
    CountDownLatch, CyclicBarrier and Semaphore
    Java Primitives and Bits
    vue 和 webstorm(01) 之 基本入门 _fei
    ubuntu 命令窗口背景颜色 #300a24 _fei
    2345 看图王ad介绍----关闭广告 _fei
    PHP 使用 pdo 操作oracle数据库 报错 _fei
    PHP 使用 pdo 操作oracle数据库 报错 _fei
  • 原文地址:https://www.cnblogs.com/crazyacking/p/4649878.html
Copyright © 2020-2023  润新知