CF558E A Simple Task
题目大意: 给定一个长度不超过10^5的字符串(小写英文字母),和不超过5000个操作。
每个操作 L R K 表示给区间[L,R]的字符串排序,K=1为升序,K=0为降序。
最后输出最终的字符串
首先这么想想,对于一段区间的排序,排完序的样子和排序之前每个字母的位置并没有关系,而是和每一个字母出现的次数有关。所以我们对于每一次操作,统计出区间中每一个字母出现了多少次,然后按字典序排序就行。更确切的说,就是这个区间中的哪一个部分都改成某一个字母,区间修改。
既然是区间修改,那么就可以用线段树实现。不过这样的话,打lazy标记就显得不是很方便。为此,我们可以开26个线段树,每一个字母开一个长度为n的权值线段树,如果第i为是这个字母,我们就把这一位改成1,然后统计区间中这个字母有多少个,就相当于求区间和了。至于修改,那就是将这个字母所在线段树的区间全都改成1.然后把操作区间的别的地方改成0即可。
举个栗子:
acbcaab
然后将[1, 6]按升序排序。
那么我们首先分别在a, b, c所在的线段树上查到了[1, 6]的区间和,即统计出了每个字母的出现次数。
然后排序的时候,对于a所在的线段树,我们将[1, 3]都改成了1,[4, 6]改成了0;对于b所在线段树,我们将[4, 4]改成了1,[1, 3]和[5, 6]改成了0;对于c,我们将[5, 6]改成了1,[1, 4]改成了0.
这样这个区间就排完序了。
那么怎么输出最终答案呢?
只要对于每一位,暴力的从0到26循环,看哪个字母在这一位上是1,就说明这一位是这个字母了。
配合break,时间复杂度最坏为O(nlogn * 26)
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<cstdlib>
#include<cctype>
#include<vector>
#include<stack>
#include<queue>
using namespace std;
#define enter printf("\n")
#define space printf(" ")
#define Mem(a) memset(a, 0, sizeof(a))
typedef long long ll;
typedef double db;
const int INF = 0x3f3f3f3f;
const db eps = 1e-;
const int maxn = 2e7 + ;
inline int read()
{
int ans = ;
char ch = getchar(), last = ' ';
while(!isdigit(ch)) {last = ch; ch = getchar();}
while(isdigit(ch))
{
ans = ans * + ch - ''; ch = getchar();
}
if(last == '-') ans = -ans;
return ans;
}
inline void write(ll x)
{
if(x < ) x = -x, putchar('-');
if(x >= ) write(x / );
putchar('' + x % );
} int n, q;
char s[]; int cnt = , root[], lson[maxn], rson[maxn], l[maxn], r[maxn];
//lson[now]和rson[now]分别记录now的左右儿子的编号,代替了now << 1和 now <<1 | 1
int sum[maxn], lazy[maxn];
void build(int& now, int L, int R) //我这个写法是先吧所有点开好了,不是动态开点(竟然比某位大佬的动态开点快)
{
now = ++cnt; lazy[now] = -;
l[now] = L; r[now] = R;
if(L == R) return;
int mid = (L + R) >> ;
build(lson[now], L, mid);
build(rson[now], mid + , R);
}
void add(int now, int id)
{
if(l[now] == r[now]) {sum[now]++; return;}
int mid = (l[now] + r[now]) >> ;
if(id <= mid) add(lson[now], id);
else add(rson[now], id);
sum[now] = sum[lson[now]] + sum[rson[now]];
}
void pushdown(int now)
{
if(lazy[now] != -) //因为lazy[now]=0代表将区间都改为0,所以没有标记要换一个记号
{
sum[lson[now]] = (r[lson[now]] - l[lson[now]] + ) * lazy[now];
sum[rson[now]] = (r[rson[now]] - l[rson[now]] + ) * lazy[now];
lazy[lson[now]] = lazy[now];
lazy[rson[now]] = lazy[now];
lazy[now] = -;
} }
void update(int now, int L, int R, int d)
{
if(L == l[now] && R == r[now])
{
sum[now] = (r[now] - l[now] + ) * d;
lazy[now] = d; return;
}
pushdown(now);
int mid = (l[now] + r[now]) >> ;
if(R <= mid) update(lson[now], L, R, d);
else if(L > mid) update(rson[now], L, R, d);
else {update(lson[now], L, mid, d); update(rson[now], mid + , R, d);}
sum[now] = sum[lson[now]] + sum[rson[now]];
}
int query(int now, int L, int R)
{
if(!sum[now]) return ; //优化
if(L == l[now] && R == r[now]) return sum[now];
pushdown(now);
int mid = (l[now] + r[now]) >> ;
if(R <= mid) return query(lson[now], L, R);
else if(L > mid) return query(rson[now], L, R);
else return query(lson[now], L, mid) + query(rson[now], mid + , R);
} int main()
{
n = read(); q = read();
scanf("%s", s + );
for(int i = ; i < ; ++i) build(root[i], , n);
for(int i = ; i <= n; ++i) add(root[s[i] - 'a'], i);
for(int i = ; i <= q; ++i)
{
int L = read(), R = read(), k = read();
if(k)
{
int pre = L - ;
for(int j = ; j < ; ++j) //枚举每一棵线段树
{
int ssum = query(root[j], L, R);
if(ssum)
{
update(root[j],L,R,); //先都改成0,在局部覆盖1
update(root[j], pre + , pre + ssum, );
}
pre += ssum;
}
}
else
{
int pre = L - ;
for(int j = ; j >= ; --j) //降序,就倒着枚举
{
int ssum = query(root[j], L, R);
if(ssum)
{
update(root[j],L,R,);
update(root[j], pre + , pre + ssum, );
}
pre += ssum;
}
}
}
for(int i = ; i <= n; ++i) //很暴力的查询
for(int j = ; j < ; ++j)
if(query(root[j], i, i)) {printf("%c", 'a' + j); break;}
enter;
return ;
}
这道题时限5秒,然而还特别容易TLE,所以得做好常数优化工作。
据说某位大佬线段树上每个节点记录26个字母出现的情况,所以只开了一棵线段树,自然就十分的快了,毫无TLE的烦恼。(很显然,我不会写,要不就不讲上述的方法了……)
CF558E A Simple Task的更多相关文章
- CF558E A simple task 线段树
这道题好猥琐啊啊啊啊啊啊 写了一个上午啊啊啊啊 没有在update里写pushup啊啊啊啊 题目大意: 给你一个字符串s,有q个操作 l r 1 :把sl..rsl..r按升序排序 l r 0 :把s ...
- 计数排序 + 线段树优化 --- Codeforces 558E : A Simple Task
E. A Simple Task Problem's Link: http://codeforces.com/problemset/problem/558/E Mean: 给定一个字符串,有q次操作, ...
- HDU-1339 A Simple Task
http://acm.hdu.edu.cn/showproblem.php?pid=1339 正常做法超时,要有点小技巧存在. A Simple Task Time Limit: 2000/1000 ...
- A Simple Task
A Simple Task Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Tot ...
- Codeforces 558E A Simple Task (计数排序&&线段树优化)
题目链接:http://codeforces.com/contest/558/problem/E E. A Simple Task time limit per test5 seconds memor ...
- Codeforces Round #312 (Div. 2) E. A Simple Task 线段树
E. A Simple Task 题目连接: http://www.codeforces.com/contest/558/problem/E Description This task is very ...
- Codeforces Round #312 (Div. 2) E. A Simple Task 线段树+计数排序
题目链接: http://codeforces.com/problemset/problem/558/E E. A Simple Task time limit per test5 secondsme ...
- 【题解】 CF11D A Simple Task
[题解] CF11D A Simple Task 传送门 \(n \le 20\) 考虑状态压缩\(dp\). 考虑状态,\(dp(i,j,O)\)表示从\(i\)到\(j\)经过点集\(O\)的路径 ...
- Codeforces 558E A Simple Task(权值线段树)
题目链接 A Simple Task 题意 给出一个小写字母序列和若干操作.每个操作为对给定区间进行升序排序或降序排序. 考虑权值线段树. 建立26棵权值线段树.每次操作的时候先把26棵线段树上的 ...
随机推荐
- layui上传图片接口
mvc中 前台调用接口 url:"../upload/uploadfiles/" 然后开始接口 代码: string a = ""; try { HttpFil ...
- Tomcat服务器的安装配置图文教程(推荐)
现在将使用Tomcat的一些经验和心得写到这里,作为记录和备忘.如果有朋友看到,也请不吝赐教. 1.首先是Tomcat的获取和安装. 获取当然得上Apache的官方网站下载,开源免费,而且带宽也足够. ...
- [日常] Go语言圣经--包和文件-导入包习题
1.每个包都有一个全局唯一的导入路径 2.按照惯例,一个包的名字和包的导入路径的最后一个字段相同 练习 2.2: 写一个通用的单位转换程序,用类似cf程序的方式从命令行读取参数,如果缺省的话则是从标准 ...
- [css3] 看博客学习别人的旋转的星球
定义一个div 太阳轨道sunline,边框显示出来,定义position为relative #sunline{ width: 500px; height: 500px; border:2px sol ...
- 【C#数据结构系列】查找
一:查找 1.1 基本概念和术语 查找(Search)是在数据结构中确定是否存在关键码等于给定关键码的记录的过程.关键码有主关键码和次关键码.主关键码能够唯一区分各个不同的记录,次关键码通常不能唯一区 ...
- Java虚拟机 - 语法糖
[深入Java虚拟机]之六:Java语法糖 语法糖(Syntactic Sugar),也称糖衣语法,是由英国计算机学家Peter.J.Landin发明的一个术语,指在计算机语言中添加的某种语法,这种语 ...
- java 非阻塞算法实现基础:unsafe类介绍
一.为什么要有Unsfae.我们为什么要了解这个类 1. java通常的代码无法直接使用操作底层的硬件,为了使java具备该能力,增加了Unsafe类 2.java的并发包中底层大量的使用这个类的功能 ...
- Flask如何给多个视图函数增加装饰器
这几天在学习Flask, 遇到了些小问题,比如说怎么给多个视图函数加相同的装饰器 给单独一个视图函数加装饰器的话很简单,写一个装饰器,然后直接加在原装饰器下面即可,多个的话,会报这样一个错误: 这个异 ...
- 第一章 深入.NET框架
一. .NET的过人之处 1..NET框架提高了软件的可重复行 ,可扩展性,可维护行和灵活性. 2.对web应用的强大支撑. 3.对Web Service(Web服务)的支持. 4.实现SOA,支持云 ...
- css中计数器的实现-笔记
原文参考http://mp.weixin.qq.com/s?__biz=MzU3MDA0NTMzMA==&mid=2247485533&idx=1&sn=e88dc5fffa6 ...