string 线段树
碰到题面就回忆起线段树专题的排序,然而模完样例发现思路无关。然后又想到splay里的文艺平衡树,然而那个是区间翻转。草草的敲了快排,发现自己可以码归并比比速度(自己sb一样,明明都是O(n*logn)),然后kx地拍了一整场考试,速度都差不多。正解是桶排+线段树优化。考虑桶排,在原专题中的排序一题中好多人都靠桶排水过了,但其实时间是6000ms,(hiahia)这题直接桶排暴力和sort一个分。所以考虑正解的思考过程,桶排排序很快,但预处理,加统计到序列中都是O(n)。但仔细斟酌可以发现这两个操作都是处理区间问题,一个查询,一个修改。但泛泛的说还不够,你会发现你不明确线段树存什么,想想桶排,桶数组是思路的源泉,而且只有26种元素,那么答案显而易见了,开26颗对应桶下标的线段树。修改是覆盖成1,查询同时再覆盖成0。思路很棒!
#include<cstdio>
#include<iostream>
#define MAXN 100010
#define TR (MAXN<<2)
using namespace std;
inline int read(){
int s=0;char ch=getchar();
while(ch<'0'||ch>'9')ch=getchar();
while(ch>='0'&&ch<='9')s=s*10+ch-'0',ch=getchar();
return s;
}
#define kd (read())
int n,m;
int tong[27];
int sm[27][TR],lb[27][TR];
#define ls (u<<1)
#define rs (u<<1|1)
inline void up(int u,int id){
sm[id][u]=sm[id][ls]+sm[id][rs];
}
inline void down(int u,int l,int r,int id){
if(!lb[id][u])return ;
int mid=l+r>>1;
lb[id][ls]=lb[id][rs]=lb[id][u];
if(lb[id][u]==-1)
sm[id][rs]=sm[id][ls]=0;
else{
sm[id][ls]=mid-l+1;
sm[id][rs]=r-mid;
}
lb[id][u]=0;
}
void upd(int u,int l,int r,int x,int y,int id){
if(l>=x&&r<=y){
sm[id][u]=(r-l+1);
lb[id][u]=1;
return ;
}
down(u,l,r,id);
int mid=l+r>>1;
if(x<=mid)upd(ls,l,mid,x,y,id);
if(y>=mid+1)upd(rs,mid+1,r,x,y,id);
up(u,id);
}
int query(int u,int l,int r,int x,int y,int id){
if(!sm[id][u])return 0;
if(l>=x&&r<=y){
int res=sm[id][u];
sm[id][u]=0;
lb[id][u]=-1;
return res;
}
down(u,l,r,id);
int mid=l+r>>1;
int res=0;
if(x<=mid)res+=query(ls,l,mid,x,y,id);
if(y>=mid+1)res+=query(rs,mid+1,r,x,y,id);
up(u,id);
return res;
}
void wks(int l,int r){
for(int i=1;i<=26;++i)
tong[i]=query(1,1,n,l,r,i);
int p=l;
for(int i=1;i<=26;++i)
if(tong[i]){
upd(1,1,n,p,p+tong[i]-1,i);
p=p+tong[i];
}
}
void wkj(int l,int r){
for(int i=1;i<=26;++i)
tong[i]=query(1,1,n,l,r,i);
int p=l;
for(int i=26;i>=1;--i)
if(tong[i]){
upd(1,1,n,p,p+tong[i]-1,i);
p=p+tong[i];
}
}
void pt(int u,int l,int r){
if(l==r){
for(int i=1;i<=26;++i)
if(sm[i][u]){putchar(i+'a'-1);break;}
return ;
}
for(int i=1;i<=26;++i)
down(u,l,r,i);
int mid=l+r>>1;
pt(ls,l,mid);pt(rs,mid+1,r);
}
int main(){
// freopen("da.in","r",stdin);
n=kd;m=kd;
char rn;
for(int i=1;i<=n;++i){
rn=getchar();
upd(1,1,n,i,i,(rn-'a'+1));
}
int l,r,opt;
for(int i=1;i<=m;++i){
l=kd;r=kd;opt=kd;
switch(opt){
case 1:{wks(l,r);break;}
case 0:{wkj(l,r);break;}
}
}
pt(1,1,n);
puts("");
}
线段树习惯不好,down函数最好放在判断下面,此时才能保证节点数为MAXN×4