题意大概是给你一个字符串,1e5次修改,每次给一个区间升序排列或降序排列,最后输出这个字符串;

其实是个挺裸的线段树优化题;但是我没有意识去结合桶排,扑该.....

首先 1.40分算法

O(NMlogN)

 inline void update(int k){
for(int i=;i<=;++i){
tmp[k].tong[i]=tmp[ls].tong[i]+tmp[rs].tong[i];
}
} void ud(int k,int l,int r,int L,int R){
if(L<=l&&r<=R)return void();
if(L<=mid)ud(ls,l,mid,L,R);
if(R>mid)ud(rs,mid+,r,L,R);
update(k);

直接sort

 #include<bits/stdc++.h>
using namespace std;
#define val(x) (x-'a'+1)
int s[],N,M;
char S[];
bool cmp1(int a,int b){return a<b;}
bool cmp0(int a,int b){return a>b;}
int main()
{
//freopen("da.in","r",stdin);
// freopen("ac.out","w",stdout);
scanf("%d%d",&N,&M);
scanf("%s",S+);
for(int i=;i<=N;++i)s[i]=val(S[i]);
for(int i=,l,r,opt;i<=M;++i){
scanf("%d%d%d",&l,&r,&opt);
if(!opt)sort(s+l,s+r+,cmp0);
else sort(s+l,s+r+,cmp1);
// if(clock()>999900)break;
}
char c;
for(int i=;i<=N;++i){
c=s[i]+'a'-;
printf("%c",c);
}
printf("\n");
}

其次 2.仍是40分算法:

其实也是出题人没良心,我们可以直接桶排,复杂度O(NM);起码降下了一个log,但还是40分;

代码就不提供了;

最后 3.100分算法:

思路:结合2.中的桶排思想,我们发现修改排序一次可以只有O(N)的复杂度,而N,M<=1e5,自然想到线段树优化桶排;

而这样想的裸想法的复杂度是O(MNlogN)氧化钙!跟暴力一样!

但是可以发现 你的数值N的值域是你的字母,那N就只有26啦;

最后O(MlogN*26)

分析一下  M是询问;  logN是线段树上的查询(合并桶而需查询你所修改的区域的桶来合成一个新的桶); 26是每次桶合并所需复杂度;

好,接下来,我们每次修改一个区间进行排序,就是把对应区间的桶合并成一个新桶,然后用这个新桶进行区间修改,但是注意,这个区间修改一定不要去真的O(N)区间赋值

那就成为了真正的O(NMlogN)了;

那怎么办?

我们可以打懒标记啊!别说你像我一样不会打标记......

那我们就可以标记哪个区间被修改了然后访问到再down一下,最后一遍dfs来一次全down统计答案对吧;

注意,这里down很长很长

 void down(int k){
if(!tmp[k].lazy)return ;
register int sz=len(ls),pl=,pr=;
memset(tmp[ls].tong,,sizeof(tmp[ls].tong));
memset(tmp[rs].tong,,sizeof(tmp[rs].tong));
if(tmp[k].opt){
for(int i=;i<=;++i){
if(tmp[k].tong[i]<=sz)
{
tmp[ls].tong[i]=tmp[k].tong[i],sz-=tmp[k].tong[i];
}
else {
tmp[ls].tong[i]=sz;sz=i;break;
}
}
tmp[rs].tong[sz]=tmp[k].tong[sz]-tmp[ls].tong[sz];
if(len(rs)==&&tmp[rs].tong[sz]==){pr=sz;}
register int zs=len(rs)-tmp[rs].tong[sz];
for(int i=sz+;i<=;++i){
tmp[rs].tong[i]=tmp[k].tong[i];
zs-=tmp[k].tong[i];
if(!zs)break;
}
}
else {
for(int i=;i>=;--i){
if(tmp[k].tong[i]<=sz)
{
tmp[ls].tong[i]=tmp[k].tong[i],sz-=tmp[k].tong[i];
}
else {
tmp[ls].tong[i]=sz;sz=i;break;
}
}
tmp[rs].tong[sz]=tmp[k].tong[sz]-tmp[ls].tong[sz];
register int zs=len(rs)-tmp[rs].tong[sz];
for(int i=sz-;i>=;--i){
tmp[rs].tong[i]=tmp[k].tong[i];
zs-=tmp[k].tong[i];
if(!zs)break;
}
}
if(tmp[ls].l!=tmp[ls].r)tmp[ls].lazy=;
else tmp[ls].lz=pl;tmp[ls].opt=tmp[k].opt;
if(tmp[rs].l!=tmp[rs].r)tmp[rs].lazy=;
else tmp[rs].lz=pr;tmp[rs].opt=tmp[k].opt;
tmp[k].lazy=;tmp[k].opt=;
}

当然我不像Deepin某一样缩行;

其实这里的down就是把你线段树上这个节点的两个儿子所对应区间的桶重新赋值的过程,当然赋值一次需要O(52);

Tip:

还有一点特别容易错的就是

记住每次把你对应修改的区间的桶合并后要立即修改对应区间的桶(区间修改):(这里lg这个vector存放的是你修改的区间的在线段树上的节点编号)

 void exch(int opt){
sort(lg.begin(),lg.end(),cmp);
for(int i=,sz,tt=,ts=;i<lg.size();++i){
sz=len(lg[i]);
memset(tmp[lg[i]].tong,,sizeof(tmp[lg[i]].tong));
if(opt){
for(;tt<=;++tt){
if(sz>=ans[tt]){
sz-=ans[tt];
tmp[lg[i]].tong[tt]=ans[tt];
}
else {
tmp[lg[i]].tong[tt]=sz;
ans[tt]-=sz;break;
}
}
}
else {
for(;ts>=;--ts){
if(sz>=ans[ts]){
sz-=ans[ts];
tmp[lg[i]].tong[ts]=ans[ts];
}
else {
tmp[lg[i]].tong[ts]=sz;
ans[ts]-=sz;break;
}
}
}
}
}

区间修改

然后修改完后要立即把你这些修改的区间到根update一遍,毕竟你的懒标记只能维护儿子而不能维护父亲;

 inline void update(int k){
for(int i=;i<=;++i){
tmp[k].tong[i]=tmp[ls].tong[i]+tmp[rs].tong[i];
}
} void ud(int k,int l,int r,int L,int R){
if(L<=l&&r<=R)return void();
if(L<=mid)ud(ls,l,mid,L,R);
if(R>mid)ud(rs,mid+,r,L,R);
update(k);
}

update

然后附上代码

 #include<bits/stdc++.h>
using namespace std;
#define val(x) (x-'a'+1)
#define mid (l+r>>1)
#define ls (k<<1)
#define rs (k<<1|1)
#define len(x) (tmp[x].r-tmp[x].l+1)
int s[],N,M;
char S[];
struct node{
int l;int r;
int tong[];
int lazy,lz,opt;
}tmp[<<];
int ans[];
vector<int>lg; inline void update(int k){
for(int i=;i<=;++i){
tmp[k].tong[i]=tmp[ls].tong[i]+tmp[rs].tong[i];
}
} void down(int k){
if(!tmp[k].lazy)return ;
register int sz=len(ls),pl=,pr=;
memset(tmp[ls].tong,,sizeof(tmp[ls].tong));
memset(tmp[rs].tong,,sizeof(tmp[rs].tong));
if(tmp[k].opt){
for(int i=;i<=;++i){
if(tmp[k].tong[i]<=sz)
{
tmp[ls].tong[i]=tmp[k].tong[i],sz-=tmp[k].tong[i];
// if(len(ls)==1&&tmp[ls].tong[i]){pl=i;break;}
}
else {
tmp[ls].tong[i]=sz;sz=i;break;
}
}
//for(int i=1;i<=sz-1;++i)tmp[rs].tong[i]=0;
//for(int i=sz+1;i<=26;++i)tmp[ls].tong[i]=0;
tmp[rs].tong[sz]=tmp[k].tong[sz]-tmp[ls].tong[sz];
if(len(rs)==&&tmp[rs].tong[sz]==){pr=sz;}
register int zs=len(rs)-tmp[rs].tong[sz];
for(int i=sz+;i<=;++i){
tmp[rs].tong[i]=tmp[k].tong[i];
zs-=tmp[k].tong[i];
if(!zs)break;
// if(len(rs)==1&&tmp[rs].tong[i]&&!pr)pr=i;
}
}
else {
for(int i=;i>=;--i){
if(tmp[k].tong[i]<=sz)
{
tmp[ls].tong[i]=tmp[k].tong[i],sz-=tmp[k].tong[i];
// if(len(ls)==1&&tmp[ls].tong[i]){pl=i;break;}
}
else {
tmp[ls].tong[i]=sz;sz=i;break;
}
}
//for(int i=26;i>=sz+1;--i)tmp[rs].tong[i]=0;
//for(int i=1;i<=sz-1;++i)tmp[ls].tong[i]=0;
tmp[rs].tong[sz]=tmp[k].tong[sz]-tmp[ls].tong[sz];
//if(len(rs)==1&&tmp[rs].tong[sz]==1){pr=sz;}
register int zs=len(rs)-tmp[rs].tong[sz];
for(int i=sz-;i>=;--i){
tmp[rs].tong[i]=tmp[k].tong[i];
zs-=tmp[k].tong[i];
if(!zs)break;
// if(len(rs)==1&&tmp[rs].tong[i]&&!pr)pr=i;
}
}
if(tmp[ls].l!=tmp[ls].r)tmp[ls].lazy=;
else tmp[ls].lz=pl;tmp[ls].opt=tmp[k].opt;
if(tmp[rs].l!=tmp[rs].r)tmp[rs].lazy=;
else tmp[rs].lz=pr;tmp[rs].opt=tmp[k].opt;
tmp[k].lazy=;tmp[k].opt=;
} inline void merge(int u,int opt){
down(u);
for(register int i=;i<=;++i){
ans[i]+=tmp[u].tong[i];
}
if(len(u)>)tmp[u].lazy=;lg.push_back(u);
tmp[u].opt=opt;
} inline int zip(int k){
//cout<<"laidao!"<<endl;
for(int i=;i<=;++i)if(tmp[k].tong[i])return i;
} void find(int k,int l,int r){
if(l==r)return s[l]=zip(k),void();
down(k);
find(ls,l,mid);
find(rs,mid+,r);
} void query(int k,int l,int r,int L,int R,int opt){
if(L<=l&&r<=R)return merge(k,opt),void();
down(k);
if(L<=mid)query(ls,l,mid,L,R,opt);
if(R>mid)query(rs,mid+,r,L,R,opt);
} void ud(int k,int l,int r,int L,int R){
if(L<=l&&r<=R)return void();
if(L<=mid)ud(ls,l,mid,L,R);
if(R>mid)ud(rs,mid+,r,L,R);
update(k);
} void building(int k,int l,int r){
tmp[k].l=l;tmp[k].r=r;
if(l==r)return tmp[k].l=tmp[k].r=l,tmp[k].tong[s[l]]=,tmp[k].lazy=tmp[k].opt=,void();
building(ls,l,mid);building(rs,mid+,r);
update(k);
} bool cmp(int u,int v){return tmp[u].l<tmp[v].l;} void exch(int opt){
sort(lg.begin(),lg.end(),cmp);
for(int i=,sz,tt=,ts=;i<lg.size();++i){
sz=len(lg[i]);
memset(tmp[lg[i]].tong,,sizeof(tmp[lg[i]].tong));
if(opt){
for(;tt<=;++tt){
if(sz>=ans[tt]){
sz-=ans[tt];
tmp[lg[i]].tong[tt]=ans[tt];
}
else {
tmp[lg[i]].tong[tt]=sz;
ans[tt]-=sz;break;
}
}
}
else {
for(;ts>=;--ts){
if(sz>=ans[ts]){
sz-=ans[ts];
tmp[lg[i]].tong[ts]=ans[ts];
}
else {
tmp[lg[i]].tong[ts]=sz;
ans[ts]-=sz;break;
}
}
}
}
}
int main()
{
//freopen("da.in","r",stdin);
//freopen("te.out","w",stdout);
scanf("%d%d",&N,&M);
scanf("%s",S+);
for(int i=;i<=N;++i)s[i]=val(S[i]);
building(,,N);
for(int i=,l,r,opt;i<=M;++i){
scanf("%d%d%d",&l,&r,&opt);
memset(ans,,sizeof(ans));lg.clear();
query(,,N,l,r,opt);
exch(opt);
ud(,,N,l,r);
}
find(,,N);
char c;
for(int i=;i<=N;++i){
c=s[i]+'a'-;
printf("%c",c);
}
printf("\n");
//int ppx='P'-'a'+1;
//cout<<"ppx="<<ppx<<endl;
}

ac

string [线段树优化桶排]的更多相关文章

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

    题目链接:http://codeforces.com/contest/558/problem/E E. A Simple Task time limit per test5 seconds memor ...

  2. Weak Pair---hud5877大连网选(线段树优化+dfs)

    题目链接:http://acm.split.hdu.edu.cn/showproblem.php?pid=5877  题意:给你一颗树,有n个节点,每个节点都有一个权值v[i]:现在求有多少对(u,v ...

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

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

  4. hdu 3698 UVA1490 Let the light guide us 线段树优化DP

    题目链接 and 题目大意 hdu3698 但是 hdu的数据比较弱,所以在这luogu提交吧UVA1490 Let the light guide us 有一个\(n*m\)的平原,要求每行选一个点 ...

  5. Codeforces Round #426 (Div. 2) D 线段树优化dp

    D. The Bakery time limit per test 2.5 seconds memory limit per test 256 megabytes input standard inp ...

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

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

  7. G. 神圣的 F2 连接着我们 线段树优化建图+最短路

    这个题目和之前写的一个线段树优化建图是一样的. B - Legacy CodeForces - 787D 线段树优化建图+dij最短路 基本套路 之前这个题目可以相当于一个模板,直接套用就可以了. 不 ...

  8. D - The Bakery CodeForces - 834D 线段树优化dp···

    D - The Bakery CodeForces - 834D 这个题目好难啊,我理解了好久,都没有怎么理解好, 这种线段树优化dp,感觉还是很难的. 直接说思路吧,说不清楚就看代码吧. 这个题目转 ...

  9. B - Legacy CodeForces - 787D 线段树优化建图+dij最短路 基本套路

    B - Legacy CodeForces - 787D 这个题目开始看过去还是很简单的,就是一个最短路,但是这个最短路的建图没有那么简单,因为直接的普通建图边太多了,肯定会超时的,所以要用线段树来优 ...

随机推荐

  1. vue2 学习笔记

    一.部署环境1.安装nodeJS -- 地址:https://nodejs.org/zh-cn/ 2.安装vue脚手架 (用于创建VUE项目的) 进入项目文件或者新建一个文件 npm install ...

  2. ICPC Asia Nanning 2017 F. The Chosen One (大数、规律、2的k次幂)

    Welcome to the 2017 ACM-ICPC Asia Nanning Regional Contest.Here is a breaking news. Now you have a c ...

  3. 使用render函数渲染组件

    使用render函数渲染组件:https://www.jianshu.com/p/27ec4467a66b

  4. easyUI关键(常见)组件详解

    一.easyUI 相关介绍 1.EasyUI 是前端框架,封装大量 css和封装大量 JS 2.使用前端框架时,给标签定义class 属性,就会有样式和脚本功能了(class属性对应了相关封装过的cs ...

  5. zuul开发实战(限流,超时解决)

    什么是网关 API Gateway,是系统的唯一对外的入口,介于客户端和服务器端之间的中间层,处理非业务功能 提供路由请求.鉴权.监控.缓存.限流等功能 统一接入 * 智能路由 * AB测试.灰度测试 ...

  6. 手把手 教你把H5页面打造成windows 客户端exe 软件

    序言: 好久没有更新博客了,最近在工作中碰到这种需求,由于没有做过,中间碰到好多坑,最后在一位贵人帮助的情况下,最终还是搞定了. 第一步,先安装 cefpython3 pip install cefp ...

  7. Ajax轮询请求

    Ajax轮询请求 什么是轮询? 轮询(polling):客户端按规定时间定时向服务端发送ajax请求,服务器接到请求后马上返回响应信息并关闭连接. Ajax轮询需要服务器有很快的处理速度与快速响应. ...

  8. issue - 登录前的信息和标识文件

    DESCRIPTION (描述) /etc/issue 是一个文本文件,它包含了在登录提示符出现之前显示的信息或者系统标识.如果 getty(1) 支持的话,它可能包括多个 @char 和 \char ...

  9. 一、Signalr WebApi客服-客户链接+Redis

    一.前端客服代码 <!doctype html> <html> <head> <meta charset="utf-8"> < ...

  10. Python核心编程正则表达式练习题1-1 识别后续的字符串:“bat”、“bit”、“but”、“hat”、“hit”或者“hut”

    # 1-1 识别后续的字符串:“bat”.“bit”.“but”.“hat”.“hit”或者“hut”. import re # 正则表达式,|元字符表示选择“或” # character = 'ba ...