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

猜你喜欢

转载自www.cnblogs.com/2018hzoicyf/p/11285521.html
今日推荐