\(\text{Descrption}\)
给定一个仅由小写字母组成的字符串 \(s\)。有 \(m\) 次操作,每次操作有 \(3\) 个参数 \(L,R,opt\)。如果 \(opt\) 为 \(1\),那么将区间 \([L,R]\) 内的字符按升序排列,反之按降序排列。
求 \(m\) 次操作后的字符串。
对于 \(100\%\) 的数据,\(1≤n≤10^5 , 0≤m≤10^5 ,0≤opt≤1 ,1≤L≤R≤n\)。
\(\text{Solution}\)
感觉很神奇。
其实关键就是这里的权值只有 \(26\) 种。
我们可以从小到大枚举字母分界线 \(ch\),开一棵权值线段树,大于等于 \(ch\) 的字母赋为 \(1\),反之为 \(0\)。然后依次进行 \(m\) 次排序(以 \(0,1\) 为关键字),然后我们可以 \(\mathcal O(\log n)\) 地查询某个位置是否大于等于 \(ch\),如果是就先赋为 \(ch\),这样下一次当前的 \(ch\) 变成 \(0\),不会再被赋值,后面的字母也能被赋值。
其实还有一道同名的题和这题的方法类似:\(\text{[TJOI 2016] }\)排序。
区别就是权值变成了 \([1,n]\)。我们可以二分权值,单次 \(\rm check\) 就是 \(\mathcal O(q\log n)\) 的。所以总共是 \(\mathcal O(q\log^2 n)\) 的。
另外,由于我懒得再开一篇博客,再记录一下这题线段树合并的做法。
排序后区间有序,分裂时相当于选取 权值 区间前/后 \(k\) 个数,可以用权值线段树维护。两棵线段树对应的区间排序就是线段树合并。但是我们可能不选取完整的线段树,此时需要进行分裂,用 set
维护线段树代表区间的左端点。
合并复杂度之前证过和点数相关,点数是 \((n+m)\log n\) 级别的,所以复杂度 \(\mathcal O(n\log n)\)。
\(\text{Update on 2022.7.24}\):模拟赛考到一道 trick 类似的题,这里提醒一下,它也可以用于排列计数问题。
\(\text{Code}\)
#include <cstdio>
#define rep(i,_l,_r) for(signed i=(_l),_end=(_r);i<=_end;++i)
#define print(x,y) write(x),putchar(y)
int read() {
int x=0,f=1; char s;
while((s=getchar())>'9'||s<'0') if(s=='-') f=-1;
while(s>='0'&&s<='9') x=(x<<1)+(x<<3)+(s^48),s=getchar();
return x*f;
}
void write(int x) {
if(x<0) return (void)(putchar('-'),write(-x));
if(x>9) write(x/10);
putchar(x%10^48);
}
const int maxn=1e5+5;
int n,m,L[maxn],R[maxn],op[maxn],t[maxn<<2];
char s[maxn],ans[maxn];
void pushDown(int o,int l,int r) {
int mid=l+r>>1;
if(t[o]==r-l+1) t[o<<1]=mid-l+1,t[o<<1|1]=r-mid;
else if(t[o]==0) t[o<<1]=t[o<<1|1]=0;
}
void modify(int o,int l,int r,int L,int R,int k) {
if(l>R||r<L) return;
if(l>=L&&r<=R) {t[o]=k*(r-l+1); return;}
int mid=l+r>>1;
pushDown(o,l,r);
modify(o<<1,l,mid,L,R,k); modify(o<<1|1,mid+1,r,L,R,k);
t[o]=t[o<<1]+t[o<<1|1];
}
int query(int o,int l,int r,int L,int R) {
if(l>R||r<L) return 0;
if(l>=L&&r<=R) return t[o];
int mid=l+r>>1;
pushDown(o,l,r);
return query(o<<1,l,mid,L,R)+query(o<<1|1,mid+1,r,L,R);
}
void work() {
rep(i,1,m) {
int cnt=query(1,1,n,L[i],R[i]);
if(cnt==R[i]-L[i]+1||cnt==0) continue;
if(op[i]) modify(1,1,n,L[i],R[i]-cnt,0),modify(1,1,n,R[i]-cnt+1,R[i],1);
else modify(1,1,n,L[i],L[i]+cnt-1,1),modify(1,1,n,L[i]+cnt,R[i],0);
}
}
int main() {
n=read(),m=read();
scanf("%s",s+1);
rep(i,1,m) L[i]=read(),R[i]=read(),op[i]=read();
for(char ch='a';ch<='z';++ch) {
rep(i,1,n) modify(1,1,n,i,i,s[i]>=ch);
work();
rep(i,1,n) if(query(1,1,n,i,i)) ans[i]=ch;
}
rep(i,1,n) putchar(ans[i]); puts("");
return 0;
}