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

题目链接:http://codeforces.com/contest/558/problem/E

E. A Simple Task

time limit per test
5 seconds

memory limit per test
512 megabytes

input
standard input

output
standard output

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 k = 0.

Output the final string after applying the queries.

Input

The first line will contain two integers n, q (1 ≤ n ≤ 105, 0 ≤ q ≤ 50 000),
 the length of the string and the number of queries respectively.

Next line contains a string S itself. It contains only lowercase English letters.

Next q lines will contain three integers each i, j, k (1 ≤ i ≤ j ≤ n, ).

Output

Output one line, the string S after applying the queries.

Sample test(s)

input
10 5
abacdabcda
7 10 0
5 8 1
1 4 0
3 6 0
7 10 1

output
cbcaaaabdd

input
10 1
agjucbvdfk
1 10 1

output
abcdfgjkuv

Note

First sample test explanation:

题意:就是说给你一个n长度字符串,进行q次操作,k=1时要对着一个区间的字符进行

非递减排序,k=2时相反,最后输出操作后的字符串

思路:正常写法肯定超时,那就可肯定要优化,采取线段树优化查询和更改都是log2(n)

线短路算法:https://blog.csdn.net/zearot/article/details/48299459

代码有解析,我就不多说了!

上代码:

  1 #include<stdio.h>
  2 #include<iostream>
  3 #include<algorithm>
  4 #include<string.h>
  5 using namespace std;
  6 const int maxn=100005;
  7 #define lson(x) x<<1
  8 #define rson(x) x<<1|1
  9 #define L(x) tree[x].l;
 10 #define R(x) tree[x].r;
 11 #define Hav[x] tree[x].hav
 12 #define Lazy(x) tree[x].lazy
 13 char ans[maxn],str[maxn];
 14 int cnt[27];
 15 struct Segtree  //声明26棵树来存每个字母
 16 {
 17     int l,r,hav,m,lazy;
 18 }tree[27][maxn<<2];
 19 void pushup(int root,int id)  //区间求和,因为一个父区间中某个字母的个数是所有子区间字母个数的和
 20 {
 21     tree[id][root].hav=tree[id][root<<1].hav+tree[id][root<<1|1].hav;
 22 }
 23 void pushdown(int root,int id) //下推标记,在这过程中将所个区间全部填满,(为什么填满要看主函数)
 24 {
 25     if(tree[id][root].lazy<0) return;
 26     tree[id][root<<1].hav=(tree[id][root<<1].r-tree[id][root<<1].l)*tree[id][root].lazy;
 27     tree[id][root<<1].lazy=tree[id][root].lazy;
 28     tree[id][root<<1|1].hav=(tree[id][root<<1|1].r-tree[id][root<<1|1].l)*tree[id][root].lazy;
 29     tree[id][root<<1|1].lazy=tree[id][root].lazy;
 30     tree[id][root].lazy=-1;  //父标记删除,子标记续上
 31 }
 32 //void build(int l,int r,int root,int id)
 33 //{ //建一颗总长度为r-l+1的树,其中第一个父节点区间长度最大,处于树的顶端,剩下的依次减少,且相互之间不能重复
 34 //    int mid=(l+r)>>1;
 35 //    tree[id][root].m=mid;  //记录中间节点,中后可能会使用(我后面代码有的时候没有使用)
 36 //    tree[id][root].l=l;
 37 //    tree[id][root].r=r;
 38 //    tree[id][root].lazy=-1;  //标记复原位
 39 //    if(r-l==1)   //按模板来说应该是r==l时才进入,但是主函数中字符数组存值是从0--n-1,
 40 //    {  //但是我建图的时候用的是1--n+1,其实n+1并没有,所以就相当于这个时候已经到树的最底部了
 41 //        //这一点多做一些题就会学到好些骚操作
 42 //        tree[id][root].hav=(str[l-1]==‘a‘+id);
 43 //        return;
 44 //    }
 45 //    build(l,mid,root<<1,id);
 46 //    build(mid+1,r,root<<1|1,id);
 47 //    pushup(root,id);
 48 //}
 49 void build(int l,int r,int n,int id)
 50
 51 {
 52
 53     int m=(l+r)>>1;
 54
 55     tree[id][n].l=l;
 56
 57     tree[id][n].r=r;
 58
 59     tree[id][n].m=m;
 60
 61     tree[id][n].lazy=-1;
 62     if(r-l==1)
 63     {
 64         tree[id][n].hav=(str[l-1]==‘a‘+id);
 65         return;
 66     }
 67
 68     if(r-l==1)
 69
 70     {
 71
 72         tree[id][n].hav=(str[l-1]==‘a‘+id);
 73
 74         return;
 75
 76     }
 77
 78     build(l,m,n<<1,id);
 79     //因为当(n+n+1)>>1的值为n,一旦加一,那就超范围了
 80     //而且本题再给树底部赋值的时候是用str[l-1]来判断的,由此可见,Segtree结构体里面存的范围和实际操作相差一
 81     build(m,r,n<<1|1,id);  //————————我的代码是这里错了,我写的是“m+1”——————————————
 82
 83     pushup(n,id);
 84
 85 }
 86 //void update(int l,int r,int root,int ops,int id)
 87 //{ //更新操作,把给出的这一部分区间全部填满(这里说的全部填满就是tree[id][root].hav=(tree[id][root].r-tree[id][root].l)*1(数字);)
 88 //    if(tree[id][root].l==l && tree[id][root].r==r)
 89 //    {
 90 //        tree[id][root].hav=(tree[id][root].r-tree[id][root].l)*ops;
 91 //        tree[id][root].lazy=ops;
 92 //        return;
 93 //    }
 94 //    //先下推标记,要不然有的区间还没更新就去查找就会错
 95 //    pushdown(root,id);
 96 //    int mid=(tree[id][root].l+tree[id][root].r)>>1;
 97 //    if(r<=mid) //满足这一个或下一个条件就说明所求区间就在此父节点的一个子区间内
 98 //    {
 99 //        update(l,r,root<<1,ops,id);
100 //    }
101 //    else if(l>=mid)
102 //    {
103 //        update(l,r,root<<1|1,ops,id);
104 //    }
105 //    else
106 //    {
107 //        update(l,mid,root<<1,ops,id);
108 //        update(mid,r,root<<1|1,ops,id);
109 //    }
110 //    pushup(root,id);
111 //}
112 void update(int l,int r,int n,int op,int id)
113
114 {
115
116     if(tree[id][n].l==l && tree[id][n].r==r)
117
118     {
119
120         tree[id][n].hav=(tree[id][n].r-tree[id][n].l)*op;
121
122         tree[id][n].lazy=op;
123
124         return;
125
126     }
127
128     pushdown(n,id);
129
130     if(r<=tree[id][n].m)update(l,r,n<<1,op,id);
131
132     else if(l>=tree[id][n].m)update(l,r,n<<1|1,op,id);
133
134     else
135
136     {
137
138         update(l,tree[id][n].m,n<<1,op,id);
139
140         update(tree[id][n].m,r,n<<1|1,op,id);
141
142     }
143
144     pushup(n,id);
145
146 }
147 int query(int l,int r,int n,int id)
148
149 {
150
151     if(tree[id][n].l==l && tree[id][n].r==r)
152
153         return tree[id][n].hav;
154
155     pushdown(n,id);
156
157     if(r<=tree[id][n].m)
158
159         return query(l,r,n<<1,id);
160
161     if(l>=tree[id][n].m)
162
163         return query(l,r,n<<1|1,id);
164
165     return query(l,tree[id][n].m,n<<1,id)
166
167             +query(tree[id][n].m,r,n<<1|1,id); //————————这里的tree[id][n].m也不能多加一————————————————
168
169 }
170 //int query(int l,int r,int root,int id)
171 //{  //查找和更新大多都一样
172 //    if(tree[id][root].l==l && tree[id][root].r==r)
173 //    {
174 //        return tree[id][root].hav;
175 //    }
176 //    pushdown(root,id);
177 //    int mid=(tree[id][root].l+tree[id][root].r)>>1;
178 //    if(r<=mid) return query(l,r,root<<1,id);
179 //    else if(l>=mid) return query(l,r,root<<1|1,id);
180 //    return query(l,mid,root<<1,id)+query(mid+1,r,root<<1|1,id);
181 //}
182 //int main()
183 //{
184 //    int n,q;
185 //    scanf("%d%d",&n,&q);
186 //    scanf("%s",str);
187 //    for(int i=0;i<26;++i) build(1,n+1,1,i);
188 //    int l,r,k;
189 //    while(q--)
190 //    {
191 //        scanf("%d%d%d",&l,&r,&k);
192 //        for(int i=0;i<26;++i)
193 //        {
194 //            cnt[i]=query(l,r+1,1,i);
195 //            update(l,r+1,1,0,i);
196 //        }
197 //        if(k==1)
198 //        {
199 //            int loc=l;
200 //            for(int i=0;i<26;++i)
201 //            {
202 //                if(cnt[i]) update(loc,loc+cnt[i],1,1,i);
203 //                loc+=cnt[i];
204 //            }
205 //        }
206 //        else
207 //        {
208 //            int loc=l;
209 //            for(int i=25;i>=0;--i)
210 //            {
211 //                if(cnt[i]) update(loc,loc+cnt[i],1,1,i);
212 //                loc+=cnt[i];
213 //            }
214 //        }
215 //    }
216 //    for(int i=1;i<=n;++i)
217 //    {
218 //        for(int j=0;j<26;++j)
219 //        {
220 //            if(query(i,i+1,1,j))
221 //            {
222 //                ans[i-1]=‘a‘+j;
223 //                break;
224 //            }
225 //        }
226 //    }
227 //    printf("%s\n",ans);
228 //    return 0;
229 //}
230 int main()
231
232 {
233
234     int n,q;
235
236     scanf("%d%d",&n,&q);
237
238     scanf("%s",str);
239
240     for(int i=0;i<26;i++)build(1,n+1,1,i);
241     //先按照原来的字符在数组里面的顺序对线段树进行填充
242     int l,r,k;
243
244     while(q--)
245
246     {
247
248         scanf("%d%d%d",&l,&r,&k);
249
250         for(int i=0;i<26;i++)
251
252         {
253
254             cnt[i]=query(l,r+1,1,i);
255             //找出来这个被要求的区间内有多少这个字母
256             update(l,r+1,1,0,i);
257             //再把这个区间全部初始化为0
258         }
259         //判断一下是让顺序还是逆序,如果是按顺序排,那就让填充的时候字母正着填充
260         if(k==1)
261
262         {
263
264             int loc=l;
265
266             for(int i=0;i<26;i++)
267
268             {
269
270                 if(cnt[i]>0)update(loc,loc+cnt[i],1,1,i);
271
272                 loc+=cnt[i];
273
274             }
275
276         }
277
278         else
279
280         {
281
282             int loc=l;
283
284             for(int i=25;i>=0;i--)
285
286             {
287
288                 if(cnt[i]>0)update(loc,loc+cnt[i],1,1,i);
289
290                 loc+=cnt[i];
291
292             }
293
294         }
295
296     }
297     //这个就相当于一个位置一个位置的查询
298     for(int i=1;i<=n;i++)
299
300         for(int j=0;j<26;j++)
301
302             if(query(i,i+1,1,j))
303
304             {
305
306                 ans[i-1]=‘a‘+j;
307
308                 break;
309
310             }
311
312     printf("%s\n",ans);
313
314     return 0;
315
316 }

原文地址:https://www.cnblogs.com/kongbursi-2292702937/p/10776783.html

时间: 2024-10-29 10:48:13

Codeforces 558E A Simple Task (计数排序&&线段树优化)的相关文章

计数排序 + 线段树优化 --- 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,

CF 558E(A Simple Task-计数排序+线段树)

E. A Simple Task time limit per test 5 seconds memory limit per test 512 megabytes input standard input output standard output 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

[线段树] 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 Beta Round #12 D. Ball (线段树)

题目大意: n个女性中,如果有一个女性的三维比这个女性的三维都大,这个女性就要自杀.问要自杀多少个. 思路分析: 先按照第一维排序. 然后离散化第二维,用第二维的下标建树,树上的值是第三维,更新的时候取最大值. 注意是按照第一维度从大到小进入线段树. 而且还要严格递增. 所以处理第一维度比较大小的时候要分开处理,要把相同的先判断,再更新入树. 那么如何判断这个女性是否自杀. 首先,你知道第一维度是从大到小的,所以先进树了的节点的第一维度一定更大. 再次,我们考虑第二维度,我们去树上第二维度下标大

CodeForces 52C Circular RMQ(区间循环线段树,区间更新,区间求和)

转载请注明出处:http://blog.csdn.net/u012860063 题目链接:http://codeforces.com/problemset/problem/52/C You are given circular array a0,?a1,?...,?an?-?1. There are two types of operations with it: inc(lf,?rg,?v) - this operation increases each element on the segm

POJ 3468 A Simple Problem with Integers(线段树区间更新)

题目地址:POJ 3468 打了个篮球回来果然神经有点冲动..无脑的狂交了8次WA..居然是更新的时候把r-l写成了l-r... 这题就是区间更新裸题.区间更新就是加一个lazy标记,延迟标记,只有向下查询的时候才将lazy标记向下更新.其他的均按线段树的来就行. 代码如下: #include <iostream> #include <cstdio> #include <cstring> #include <math.h> #include <stac

HDU4267 A Simple Problem with Integers 线段树/树状数组

HDU4267 A Simple Problem with Integers  线段树/树状数组 2012长春网络赛A题 Problem Description Let A1, A2, ... , AN be N elements. You need to deal with two kinds of operations. One type of operation is to add a given number to a few numbers in a given interval. T