string [线段树优化桶排]
题意大概是给你一个字符串,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 [线段树优化桶排]的更多相关文章
- Codeforces 558E A Simple Task (计数排序&&线段树优化)
题目链接:http://codeforces.com/contest/558/problem/E E. A Simple Task time limit per test5 seconds memor ...
- Weak Pair---hud5877大连网选(线段树优化+dfs)
题目链接:http://acm.split.hdu.edu.cn/showproblem.php?pid=5877 题意:给你一颗树,有n个节点,每个节点都有一个权值v[i]:现在求有多少对(u,v ...
- CodeForces 558E(计数排序+线段树优化)
题意:一个长度为n的字符串(只包含26个小字母)有q次操作 对于每次操作 给一个区间 和k k为1把该区间的字符不降序排序 k为0把该区间的字符不升序排序 求q次操作后所得字符串 思路: 该题数据规模 ...
- hdu 3698 UVA1490 Let the light guide us 线段树优化DP
题目链接 and 题目大意 hdu3698 但是 hdu的数据比较弱,所以在这luogu提交吧UVA1490 Let the light guide us 有一个\(n*m\)的平原,要求每行选一个点 ...
- 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 ...
- D. Babaei and Birthday Cake---cf629D(最长上升子序列和+线段树优化)
http://codeforces.com/problemset/problem/629/D 题目大意: 我第一反应就是求最长上升子序列和 但是数值太大了 不能直接dp求 可以用线段树优化一下 ...
- G. 神圣的 F2 连接着我们 线段树优化建图+最短路
这个题目和之前写的一个线段树优化建图是一样的. B - Legacy CodeForces - 787D 线段树优化建图+dij最短路 基本套路 之前这个题目可以相当于一个模板,直接套用就可以了. 不 ...
- D - The Bakery CodeForces - 834D 线段树优化dp···
D - The Bakery CodeForces - 834D 这个题目好难啊,我理解了好久,都没有怎么理解好, 这种线段树优化dp,感觉还是很难的. 直接说思路吧,说不清楚就看代码吧. 这个题目转 ...
- B - Legacy CodeForces - 787D 线段树优化建图+dij最短路 基本套路
B - Legacy CodeForces - 787D 这个题目开始看过去还是很简单的,就是一个最短路,但是这个最短路的建图没有那么简单,因为直接的普通建图边太多了,肯定会超时的,所以要用线段树来优 ...
随机推荐
- Maven - Maven3实战学习笔记(1)Maven使用入门
1.maven安装 1>http://maven.apache.org/download.cgi下载apache-maven-3.6.1 2>解压缩安装包到指定的文件夹,如C:\fyliu ...
- 小记---------kafka理论及命令行操作
kafka-0.10.1.X版本之前: auto.offset.reset 的值为smallest,和,largest.(offest保存在zk中) kafka-0.10.1.X版本之后: aut ...
- 为什么只有ip地址和端口号需要主机字节序到网络字节序的转换?
答复是:因为内容是二进制流,不是整数. 整数(int.uint16.uint32)的表达,是需要多字节的,在不同cpu上,字节次序是不同的.因此,从A主机到B主机,如果是异构的,就需要做字节调整.同构 ...
- web端生成的带有echarts图表的html页面,嵌入在(javaFx)webview中显示错位问题
web项目需要嵌入到手机APP的webview里面以及 windows客户端应用(JavaFx)的webview里面,这个时候就出现了问题. echarts渲染的时候根据浏览器不同的内核显示是有区别的 ...
- KL散度的理解(GAN网络的优化)
原文地址Count Bayesie 这篇文章是博客Count Bayesie上的文章Kullback-Leibler Divergence Explained 的学习笔记,原文对 KL散度 的概念诠释 ...
- jqery实现10X10的表格,双击消失
<script type="text/javascript"> $(document).ready(function(){ //循环拼接html s="&qu ...
- vue.js 分页加载,向上滑动,依次加载数据。
export default { layout: 'default', data(){ return{ page:1, pageSize:10, orderListArr:[], prodListLo ...
- vue 列表渲染 v-for
1.数组列表 v-for 块中,我们拥有对父作用域属性的完全访问权限.v-for 还支持一个可选的第二个参数为当前项的索引 1.1 普通渲染 v-for="item ...
- 集群中Session共享解决方案分析
一.为什么要Session共享 Session存储在服务器的内存中,比如Java中,Session存放在JVM的中,Session也可以持久化到file,MySQL,redis等,SessionID存 ...
- pycharm解释器链接如何pymongo
1.pymongo 链接数据库 # pycharm 链接我们的mogodb # 下载pymongo from pymongo import MongoClient # 客户端请求 服务端 # 链接 c ...