题目链接: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
代码有解析,我就不多说了!
上代码:
 #include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
using namespace std;
const int maxn=;
#define lson(x) x<<1
#define rson(x) x<<1|1
#define L(x) tree[x].l;
#define R(x) tree[x].r;
#define Hav[x] tree[x].hav
#define Lazy(x) tree[x].lazy
char ans[maxn],str[maxn];
int cnt[];
struct Segtree //声明26棵树来存每个字母
{
int l,r,hav,m,lazy;
}tree[][maxn<<];
void pushup(int root,int id) //区间求和,因为一个父区间中某个字母的个数是所有子区间字母个数的和
{
tree[id][root].hav=tree[id][root<<].hav+tree[id][root<<|].hav;
}
void pushdown(int root,int id) //下推标记,在这过程中将所个区间全部填满,(为什么填满要看主函数)
{
if(tree[id][root].lazy<) return;
tree[id][root<<].hav=(tree[id][root<<].r-tree[id][root<<].l)*tree[id][root].lazy;
tree[id][root<<].lazy=tree[id][root].lazy;
tree[id][root<<|].hav=(tree[id][root<<|].r-tree[id][root<<|].l)*tree[id][root].lazy;
tree[id][root<<|].lazy=tree[id][root].lazy;
tree[id][root].lazy=-; //父标记删除,子标记续上
}
//void build(int l,int r,int root,int id)
//{ //建一颗总长度为r-l+1的树,其中第一个父节点区间长度最大,处于树的顶端,剩下的依次减少,且相互之间不能重复
// int mid=(l+r)>>1;
// tree[id][root].m=mid; //记录中间节点,中后可能会使用(我后面代码有的时候没有使用)
// tree[id][root].l=l;
// tree[id][root].r=r;
// tree[id][root].lazy=-1; //标记复原位
// if(r-l==1) //按模板来说应该是r==l时才进入,但是主函数中字符数组存值是从0--n-1,
// { //但是我建图的时候用的是1--n+1,其实n+1并没有,所以就相当于这个时候已经到树的最底部了
// //这一点多做一些题就会学到好些骚操作
// tree[id][root].hav=(str[l-1]=='a'+id);
// return;
// }
// build(l,mid,root<<1,id);
// build(mid+1,r,root<<1|1,id);
// pushup(root,id);
//}
void build(int l,int r,int n,int id) { int m=(l+r)>>; tree[id][n].l=l; tree[id][n].r=r; tree[id][n].m=m; tree[id][n].lazy=-;
if(r-l==)
{
tree[id][n].hav=(str[l-]=='a'+id);
return;
} if(r-l==) { tree[id][n].hav=(str[l-]=='a'+id); return; } build(l,m,n<<,id);
//因为当(n+n+1)>>1的值为n,一旦加一,那就超范围了
//而且本题再给树底部赋值的时候是用str[l-1]来判断的,由此可见,Segtree结构体里面存的范围和实际操作相差一
build(m,r,n<<|,id); //————————我的代码是这里错了,我写的是“m+1”—————————————— pushup(n,id); }
//void update(int l,int r,int root,int ops,int id)
//{ //更新操作,把给出的这一部分区间全部填满(这里说的全部填满就是tree[id][root].hav=(tree[id][root].r-tree[id][root].l)*1(数字);)
// if(tree[id][root].l==l && tree[id][root].r==r)
// {
// tree[id][root].hav=(tree[id][root].r-tree[id][root].l)*ops;
// tree[id][root].lazy=ops;
// return;
// }
// //先下推标记,要不然有的区间还没更新就去查找就会错
// pushdown(root,id);
// int mid=(tree[id][root].l+tree[id][root].r)>>1;
// if(r<=mid) //满足这一个或下一个条件就说明所求区间就在此父节点的一个子区间内
// {
// update(l,r,root<<1,ops,id);
// }
// else if(l>=mid)
// {
// update(l,r,root<<1|1,ops,id);
// }
// else
// {
// update(l,mid,root<<1,ops,id);
// update(mid,r,root<<1|1,ops,id);
// }
// pushup(root,id);
//}
void update(int l,int r,int n,int op,int id) { if(tree[id][n].l==l && tree[id][n].r==r) { tree[id][n].hav=(tree[id][n].r-tree[id][n].l)*op; tree[id][n].lazy=op; return; } pushdown(n,id); if(r<=tree[id][n].m)update(l,r,n<<,op,id); else if(l>=tree[id][n].m)update(l,r,n<<|,op,id); else { update(l,tree[id][n].m,n<<,op,id); update(tree[id][n].m,r,n<<|,op,id); } pushup(n,id); }
int query(int l,int r,int n,int id) { if(tree[id][n].l==l && tree[id][n].r==r) return tree[id][n].hav; pushdown(n,id); if(r<=tree[id][n].m) return query(l,r,n<<,id); if(l>=tree[id][n].m) return query(l,r,n<<|,id); return query(l,tree[id][n].m,n<<,id) +query(tree[id][n].m,r,n<<|,id); //————————这里的tree[id][n].m也不能多加一———————————————— }
//int query(int l,int r,int root,int id)
//{ //查找和更新大多都一样
// if(tree[id][root].l==l && tree[id][root].r==r)
// {
// return tree[id][root].hav;
// }
// pushdown(root,id);
// int mid=(tree[id][root].l+tree[id][root].r)>>1;
// if(r<=mid) return query(l,r,root<<1,id);
// else if(l>=mid) return query(l,r,root<<1|1,id);
// return query(l,mid,root<<1,id)+query(mid+1,r,root<<1|1,id);
//}
//int main()
//{
// int n,q;
// scanf("%d%d",&n,&q);
// scanf("%s",str);
// for(int i=0;i<26;++i) build(1,n+1,1,i);
// int l,r,k;
// while(q--)
// {
// scanf("%d%d%d",&l,&r,&k);
// for(int i=0;i<26;++i)
// {
// cnt[i]=query(l,r+1,1,i);
// update(l,r+1,1,0,i);
// }
// if(k==1)
// {
// int loc=l;
// for(int i=0;i<26;++i)
// {
// if(cnt[i]) update(loc,loc+cnt[i],1,1,i);
// loc+=cnt[i];
// }
// }
// else
// {
// int loc=l;
// for(int i=25;i>=0;--i)
// {
// if(cnt[i]) update(loc,loc+cnt[i],1,1,i);
// loc+=cnt[i];
// }
// }
// }
// for(int i=1;i<=n;++i)
// {
// for(int j=0;j<26;++j)
// {
// if(query(i,i+1,1,j))
// {
// ans[i-1]='a'+j;
// break;
// }
// }
// }
// printf("%s\n",ans);
// return 0;
//}
int main() { int n,q; scanf("%d%d",&n,&q); scanf("%s",str); for(int i=;i<;i++)build(,n+,,i);
//先按照原来的字符在数组里面的顺序对线段树进行填充
int l,r,k; while(q--) { scanf("%d%d%d",&l,&r,&k); for(int i=;i<;i++) { cnt[i]=query(l,r+,,i);
//找出来这个被要求的区间内有多少这个字母
update(l,r+,,,i);
//再把这个区间全部初始化为0
}
//判断一下是让顺序还是逆序,如果是按顺序排,那就让填充的时候字母正着填充
if(k==) { int loc=l; for(int i=;i<;i++) { if(cnt[i]>)update(loc,loc+cnt[i],,,i); loc+=cnt[i]; } } else { int loc=l; for(int i=;i>=;i--) { if(cnt[i]>)update(loc,loc+cnt[i],,,i); loc+=cnt[i]; } } }
//这个就相当于一个位置一个位置的查询
for(int i=;i<=n;i++) for(int j=;j<;j++) if(query(i,i+,,j)) { ans[i-]='a'+j; break; } printf("%s\n",ans); return ; }
 

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

  1. CodeForces 558E(计数排序+线段树优化)

    题意:一个长度为n的字符串(只包含26个小字母)有q次操作 对于每次操作 给一个区间 和k k为1把该区间的字符不降序排序 k为0把该区间的字符不升序排序 求q次操作后所得字符串 思路: 该题数据规模 ...

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

    E. A Simple Task Problem's Link: http://codeforces.com/problemset/problem/558/E Mean: 给定一个字符串,有q次操作, ...

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

    http://codeforces.com/problemset/problem/558/E Examples input 1 abacdabcda output 1 cbcaaaabdd input ...

  4. Nowcoder Hash Function ( 拓扑排序 && 线段树优化建图 )

    题目链接 题意 : 给出一个哈希表.其避免冲突的方法是线性探测再散列.现在问你给出的哈希表是否合法.如果合法则输出所有元素插入的顺序.如果有多解则输出字典序最小的那一个.如果不合法则输出 -1 分析 ...

  5. Codeforces 1603D - Artistic Partition(莫反+线段树优化 dp)

    Codeforces 题面传送门 & 洛谷题面传送门 学 whk 时比较无聊开了道题做做发现是道神题( 介绍一种不太一样的做法,不观察出决策单调性也可以做. 首先一个很 trivial 的 o ...

  6. Codeforces.1045A.Last chance(最大流ISAP 线段树优化建图)

    题目链接 \(Description\) 你需要用给定的\(n\)个武器摧毁\(m\)架飞船中的某一些.每架飞船需要被摧毁恰好一次. 武器共三种:1.可以在给定的集合中摧毁一架飞船:2.可以摧毁区间\ ...

  7. Codeforces 558E A Simple Task(权值线段树)

    题目链接  A Simple Task 题意  给出一个小写字母序列和若干操作.每个操作为对给定区间进行升序排序或降序排序. 考虑权值线段树. 建立26棵权值线段树.每次操作的时候先把26棵线段树上的 ...

  8. codeforces 558E A Simple Task 线段树

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

  9. Codeforces 558E A Simple Task

    题意:给定一个字符串,以及m次操作,每次操作对字符串的一个子区间进行升序或降序排序,求m次操作后的串 考虑桶排,发现线段树可以模拟桶排的过程,所以对26个字母分别建立线段树即可 #include< ...

随机推荐

  1. javascript深入浅出——学习笔记(包装对象和类型检测)

    3包装对象:https://www.imooc.com/video/5676 当我们尝试把基本类型已对象的方式访问时,javascript会把该基本类型转换为对应的包装类型对象(临时对象),相当于ne ...

  2. 熟悉常用的HDFS操作

    编程实现以下指定功能,并利用Hadoop提供的Shell命令完成相同任务: 在本地Linux文件系统的“/home/hadoop/”目录下创建一个文件txt,里面可以随意输入一些单词. 在本地查看文件 ...

  3. 最长公共子序列(POJ1458)

    给出两个字符串,求出这样的一个最长的公共子序列的长度:子序列中的每个字符都能在两个原串中找到,而且每个字符的先后顺序和原串中的先后顺序一致. Sample Input: abcfbc abfcabpr ...

  4. IFE第二天

    HTML是超文本标记语言,HTML5是下一代的HTML标准. HTML元素是组成HTML文档的部分,HTML属性为HTML元素提供附加信息. 文档类型<!DOCTYPE>声明帮助浏览器正确 ...

  5. layui table默认选中指定行

    表格默认选中行,在回调里写入 done: function (res, curr, count) { tableData = res.data; $("[data-field='id']&q ...

  6. 怎么用MATLAB产生FPGA所需的hamming窗系数

    需求 在FPGA处理中如果需要对待处理数据加窗,则需要窗系数存储在ROM中以供使用. 前言 MATLAB窗函数说明 流程 比如加个hamming窗,8192点,16bit放大,最终系数18bit位宽. ...

  7. emwin之创建窗口与窗口回调函数的句柄是一致的

    @2019-04-28 [小记] 由函数GUI_CreateDialogBox 创建的窗口所返回的句柄与回调函数形参中的窗口句柄参数是一样的

  8. HAOI2018苹果树

    题解 首先所有生成树的情况树是\(n!\)的,因为第一次有1中方法,第二次有两种放法,以此类推... 然后我们发现距离这种东西可以直接枚举每条边算贡献. 于是我们枚举了一个点\(i\),又枚举了这个点 ...

  9. vscode笔记(一)- vscode自动生成文件头部注释和函数注释

    VsCode 自动生成文件头部注释和函数注释 作者:狐狸家的鱼 本文链接:vscode自动生成文件头部注释和函数注释 GitHub:sueRimn 1.安装插件KoroFileHeader 2.设置 ...

  10. Java面试题[转载]

    目录 转载 简历篇 请自我介绍 请介绍项目 基础篇 基本功 面向对象的特征 final, finally, finalize 的区别 int 和 Integer 有什么区别 重载和重写的区别 抽象类和 ...