CF558E A Simple Task
Link
Solution
和之前有道省选题很像,同样是用线段树来实现排序操作。但这道题不同之处在于值域很小,以致于每个数的个数可以直接分别用线段树统计出来,这正是这道题的突破口。后面的就很好想了,每次对一个区间排序只需要统计所有字母的出现次数,然后按顺序区间覆盖,易知复杂度是 \(O(n\log{n})\) 的,带一个 26 的大常数(显然跑不满)。
#include<stdio.h>
#include<string.h>
#define N 100007
#define lid id<<1
#define rid id<<1|1
inline int read(){
int x=0,flag=1; char c=getchar();
while(c<'0'||c>'9'){if(c=='-') flag=0;c=getchar();}
while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-48;c=getchar();}
return flag? x:-x;
}
struct Node{
int s[30],cov,l,r,mid;
}t[N<<2];
char s[N];
int n,m;
void merge(Node &id,Node x,Node y){
for(int i=1;i<=26;i++)
id.s[i]=x.s[i]+y.s[i];
}
void build(int id,int lf,int rf){
t[id].l=lf,t[id].r=rf;
if(lf==rf) t[id].s[s[lf]-'a'+1]=1;
else{
t[id].mid=(lf+rf)>>1;
build(lid,lf,t[id].mid);
build(rid,t[id].mid+1,rf);
merge(t[id],t[lid],t[rid]);
}
}
void push(int id,int val){
t[id].cov=val;
for(int i=1;i<=26;i++) t[id].s[i]=0;
t[id].s[val]=t[id].r-t[id].l+1;
}
void pushdown(int id){
push(lid,t[id].cov);
push(rid,t[id].cov);
t[id].cov=0;
}
int l,r,val;
Node query(int id){
if(l<=t[id].l&&t[id].r<=r) return t[id];
if(t[id].cov) pushdown(id);
if(r<=t[id].mid) return query(lid);
else if(l>t[id].mid) return query(rid);
Node tmp;
merge(tmp,query(lid),query(rid));
return tmp;
}
void modify(int id){
if(l<=t[id].l&&t[id].r<=r) push(id,val);
else{
if(t[id].cov) pushdown(id);
if(l<=t[id].mid) modify(lid);
if(r>t[id].mid) modify(rid);
merge(t[id],t[lid],t[rid]);
}
}
void print(){
for(int i=1;i<=n;i++){
l=r=i;
Node ret=query(1);
for(int i=1;i<=26;i++)
if(ret.s[i]) putchar('a'+i-1);
}
// putchar('\n');
}
int main(){
n=read(),m=read();
scanf("%s",s+1);
build(1,1,n);
int op;
for(int i=1;i<=m;i++){
l=read(),r=read(),op=read();
Node ret=query(1);
if(op){
int l_,r_=l-1;
for(int i=1;i<=26;i++){
if(!ret.s[i]) continue;
l=r_+1,r=r_+ret.s[i],val=i,modify(1);
r_=r;
}
}else{
int l_,r_=l-1;
for(int i=26;i;i--){
if(!ret.s[i]) continue;
l=r_+1,r=r_+ret.s[i],val=i,modify(1);
r_=r;
}
}
// print();
}
print();
}