题意:
给定长度为n的字符串,只由小写字母构成,
q次操作,操作有两种:
(l,r,0),将区间[l,r]按递减顺序排序
(l,r,1),将区间[l,r]按递增顺序排序
输出q次操作之后的串
数据范围:n<=1e5
解法:
一直在想排序不是只能暴力维护吗,
结果这题居然真是暴力。
因为字符集大小只有26,那么开26棵线段树维护每种字符的位置,
当进行排序时,在26棵线段树的对应区间统计字符数量
统计之后清空所有线段树的这个区间,
然后按照递增或递减的要求依次从左到右进行字符重新区间赋值即可。
code:
#include<bits/stdc++.h>
using namespace std;
const int maxm=3e5+5;
int temp[maxm][26];
char s[maxm];
int n,q;
struct ST{
int a[maxm<<2];
int laz[maxm<<2];
void pushup(int node){
a[node]=a[node*2]+a[node*2+1];
}
void pushdown(int node,int l,int r){
if(laz[node]!=-1){
laz[node*2]=laz[node*2+1]=laz[node];
int mid=(l+r)/2;
a[node*2]=(mid-l+1)*laz[node],a[node*2+1]=(r-mid)*laz[node];
laz[node]=-1;
}
}
void build(int l,int r,int node,int k){
laz[node]=-1;
if(l==r){
a[node]=temp[l][k];
return ;
}
int mid=(l+r)/2;
build(l,mid,node*2,k);
build(mid+1,r,node*2+1,k);
pushup(node);
}
void update(int st,int ed,int val,int l,int r,int node){
if(st<=l&&ed>=r){
a[node]=(r-l+1)*val;
laz[node]=val;
return ;
}
pushdown(node,l,r);
int mid=(l+r)/2;
if(st<=mid)update(st,ed,val,l,mid,node*2);
if(ed>mid)update(st,ed,val,mid+1,r,node*2+1);
pushup(node);
}
int ask(int st,int ed,int l,int r,int node){
if(st<=l&&ed>=r)return a[node];
pushdown(node,l,r);
int mid=(l+r)/2;
int ans=0;
if(st<=mid)ans+=ask(st,ed,l,mid,node*2);
if(ed>mid)ans+=ask(st,ed,mid+1,r,node*2+1);
pushup(node);
return ans;
}
}t[26];
signed main(){
scanf("%d%d",&n,&q);
scanf("%s",s+1);
for(int i=1;i<=n;i++){
temp[i][s[i]-'a']=1;
}
for(int i=0;i<26;i++){
t[i].build(1,n,1,i);
}
while(q--){
int l,r,k;scanf("%d%d%d",&l,&r,&k);
int cnt[26]={0};
for(int i=0;i<26;i++){
cnt[i]=t[i].ask(l,r,1,n,1);//查询
t[i].update(l,r,0,1,n,1);//清空
}
if(k==0){//递减
for(int i=25;i>=0;i--){
if(!cnt[i])continue;
t[i].update(l,l+cnt[i]-1,1,1,n,1);
l+=cnt[i];
}
}else if(k==1){//递增
for(int i=0;i<26;i++){
if(!cnt[i])continue;
t[i].update(l,l+cnt[i]-1,1,1,n,1);
l+=cnt[i];
}
}
}
for(int i=1;i<=n;i++){
for(int j=0;j<26;j++){
if(t[j].ask(i,i,1,n,1)){
s[i]='a'+j;
break;
}
}
}
printf("%s\n",s+1);
return 0;
}