计数排序 + 线段树优化 --- 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;
}
时间: 2024-08-11 17:59:45

计数排序 + 线段树优化 --- Codeforces 558E : A Simple Task的相关文章

Codeforces 558E A Simple Task (计数排序&amp;&amp;线段树优化)

题目链接:http://codeforces.com/contest/558/problem/E E. A Simple Task time limit per test5 seconds memory limit per test512 megabytes inputstandard input outputstandard output This task is very simple. Given a string S of length n and q queries each quer

[线段树] codeforces 558E. A Simple Task

题意: 给一个长度n的字符串,q次操作,每次操作把[l,r]排序,k=0非递增,k=1非递减. 题解: 采用计数排序的复杂度是O(n?q),无法通过,但有所启示. 可以看出计数就是区间求和,排序就是区间更新,可以用线段树维护. 做法是建立26棵线段树,第i棵树维护第i个字母的位置信息. 计数时,在26棵线段树内分别做一次查询,排序时根据递增还是递减,把相应的区间赋值为相应的字母. #include<bits/stdc++.h> #define lson rt<<1,l,mid #d

codeforces 558E A Simple Task 线段树

题目链接 题意较为简单. 思路: 由于仅仅有26个字母,所以用26棵线段树维护就好了,比較easy. #include <iostream> #include <string> #include <vector> #include <cstring> #include <cstdio> #include <map> #include <queue> #include <algorithm> #include &

Codeforces 558E A Simple Task

Discription This task is very simple. Given a string S of length n and q queries each query is on the format i j k which means sort the substring consisting of the characters from i to j in non-decreasing order if k?=?1 or in non-increasing order if 

Codeforces 787D. Legacy 线段树优化建图+最短路

output standard output Rick and his co-workers have made a new radioactive formula and a lot of bad guys are after them. So Rick wants to give his legacy to Morty before bad guys catch them. There are n planets in their universe numbered from 1 to n.

bzoj5017 [Snoi2017]炸弹 (线段树优化建图+)tarjan 缩点+拓扑排序

题目传送门 https://lydsy.com/JudgeOnline/problem.php?id=5017 题解 这个题目方法挺多的. 线段树优化建图 线段树优化建图的做法应该挺显然的,一个炸弹能够引爆的炸弹的显然应该是一个区间里面的,直接对这个区间进行线段树优化建图. 这样可以得到一个带环图,缩点以后这个炸弹能够炸到的炸弹就是从这个点能够走到的点. 但是这个不太好做,不过可以发现最终的炸弹也是一个区间,所以可以通过缩点后的 DAG 来求出左右端点. 时间复杂度 \(O(n\log n)\)

HDU4719-Oh My Holy FFF(DP线段树优化)

Oh My Holy FFF Time Limit: 5000/2500 MS (Java/Others)    Memory Limit: 65535/65535 K (Java/Others) Total Submission(s): 606    Accepted Submission(s): 141 Problem Description N soldiers from the famous "*FFF* army" is standing in a line, from le

D. Babaei and Birthday Cake---cf629D(最长上升子序列和+线段树优化)

http://codeforces.com/problemset/problem/629/D 题目大意: 我第一反应就是求最长上升子序列和  但是数值太大了  不能直接dp求  可以用线段树优化一下 #include<stdio.h> #include<string.h> #include<stdio.h> #include<math.h> #include<iostream> #include<algorithm> using na

UVA 11134 - Fabled Rooks(贪心 / 二分图 + 线段树优化连边)

题目地址:Fabled Rooks 题目大意:n * n 的棋盘上摆了 n <=10^5 个车,让他们两两不攻击,每个车必须摆在一个给定矩形里,给出一个解决方案? 1. 贪心 由于行列互不影响, 所以可以分两遍求.第一遍确定每个车的行数,第二遍确定列数. 以行为例,若从左到右扫描,则按照区间的右端点升序排序,因为如果扫到一个位置两枚棋子都可以放,则选择右端点较小的那个(右端点大的后面还有机会). 2. 二分图匹配 有个毒瘤老师把题目改成了这样:n * n 的棋盘上摆了 n <=10^5 个车,