\(\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;
}
posted on 2020-11-08 15:56  Oxide  阅读(94)  评论(0编辑  收藏  举报