碰到题面就回忆起线段树专题的排序,然而模完样例发现思路无关。然后又想到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