rt,思路是建 26 棵线段树,具体思路见注释
可是本地运行样例爆栈,目前鉴定为 query 函数有问题,求助,谢谢
#include<iostream>
#define maxn 100001
using namespace std;
#define lson i<<1
#define rson i<<1|1
int n,m,tmp[maxn]; char c[maxn];
// tmp 起一个桶的作用,用于保存上次的查询结果(因为要推平)
struct node{ int l,r,t,lazy; }tree[27][maxn<<2];
// t 用于维护 "该树维护的字母" 在 [l,r] 区间内出现的次数。
// 树的核心操作是查询与区间推平。
void pushup(int id,int i){ tree[id][i].t=tree[id][lson].t+tree[id][rson].t; }
void pushdown(int id,int i){ // 推平 pushdown
tree[id][lson].t=tree[id][i].lazy*(tree[id][lson].r-tree[id][lson].l+1);
tree[id][rson].t=tree[id][i].lazy*(tree[id][rson].r-tree[id][rson].l+1);
tree[id][lson].lazy=tree[id][rson].lazy=tree[id][i].lazy;
tree[id][i].lazy=0;
}
void build(int id,int i,int l,int r){
// id:树的标号(因为有26棵线段树)id:96 即为所维护的字母的 ASCII
tree[id][i].l=l,tree[id][i].r=r;
if (l==r){
if (c[l]-96==id){ // c[l]是这棵树维护的字母。
tree[id][i].t=1;
}
else {
tree[id][i].t=0;
}
return;
}
int mid=l+r>>1;
build(id,lson,l,mid);
build(id,rson,mid+1,r);
pushup(id,i);
}
int query(int id,int i,int l,int r){
// 查询该字母在该区间内出现的次数,就是一个普通的查询
if (l<=tree[id][i].l && tree[id][i].r<=r){
return tree[id][i].t;
}
pushdown(id,i);
int mid=(tree[id][i].l+tree[id][i].r)>>1,val=0;
if (l<=mid) val+=query(id,lson,l,r);
if (r>mid) val+=query(id,rson,l,r);
return val;
}
void assign(int id,int i,int l,int r,int k){
// 要对这段区间进行排序,就要先把它推平成0,即变成该区间内没有一个该字母的状态
// 然后再次调用 assign 函数,根据查询结果重新推平赋值,把字母放到正确的区间。
// assign 就是推平函数。
if (l<=tree[id][i].l && tree[id][i].r<=r){
tree[id][i].t=k*(tree[id][i].r-tree[id][i].l+1);
tree[id][i].lazy=k;
return;
}
pushdown(id,i);
int mid=(tree[id][i].l+tree[id][i].r)>>1;
if (mid>=l) assign(id,lson,l,r,k);
if (mid<r) assign(id,rson,l,r,k);
pushup(id,i);
}
int main(){
cin>>n>>m>>c;
for (int i=1;i<=26;i++){ // 26棵线段树一一建树。
build(i,1,0,n-1); // char 数组下标从 0 开始
}
int l,r,opt,sum;
for (int i=1;i<=m;i++){
cin>>l>>r>>opt; // sum 用于标记已经推到了多少个位置
l-=1,r-=1,sum=1; // 因为 C数组从0开始,所以-1。
if (opt==1){ // 升序
for (int i=1;i<=26;i++){ // 每棵树都要查一遍
tmp[i]=query(i,1,l,r); // 出现了多少次
assign(i,1,l,r,0); // 推为 0
assign(i,1,sum,sum+tmp[i]-1,1);
/* k 传 1?
因为 assign 里面赋的值是 区间长*k,而不是直接赋 k
传 k 代表:在这个区间出现了 (sum+tmp[i]-1 -sum +1)*1 = tmp[i] 次
而 tmp[i] 就是大区间内存在该字母的个数 */
}
}
if (opt==0){ // 降序:从 z 查到 a
for (int i=26;i>=1;i--){ // 每棵树都要查一遍
tmp[i]=query(i,1,l,r); // 出现了多少次
assign(i,1,l,r,0); // 推为 0
assign(i,1,sum,sum+tmp[i]-1,1);
}
}
}
// 暴力输出
for (int i=1;i<=n;i++){ // 枚举整个字符串
for (int j=1;j<=26;j++){ // 搜 26 个字母
if (query(j,1,i,i)){
cout<<char(j+96); break;
}
}
}
return 0;
}