传送门
ODTODTODT水题。
题意:有一个字母序列,支持区间赋值,查询区间某个字母的数量,区间按字母序排序。
思路:
可以开262626棵线段树搞过去,然而也可以用ODTODTODT秒掉。
如果用ODTODTODT排序操作可以直接上桶排感觉快到飞起。
不会ODTODTODT的点这儿
代码:
#include<bits/stdc++.h>
#define ri register int
#define fi first
#define se second
using namespace std;
inline int read(){
int ans=0;
char ch=getchar();
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
return ans;
}
int n,m;
struct Node{
int l,r;
mutable int v;
Node(int l,int r=-1,int v=0):l(l),r(r),v(v){}
friend inline bool operator<(const Node&a,const Node&b){return a.l<b.l;}
};
set<Node>S;
typedef set<Node>::iterator It;
typedef pair<int,int> pii;
vector<pii>q;
inline It split(int pos){
It it=S.lower_bound(pos);
if(it!=S.end()&&it->l==pos)return it;
--it;
if(pos>it->r)return S.end();
int l=it->l,r=it->r,v=it->v;
return S.erase(it),S.insert(Node(l,pos-1,v)),S.insert(Node(pos,r,v)).first;
}
inline void assign(int l,int r,int v){
It R=split(r+1),L=split(l);
S.erase(L,R),S.insert(Node(l,r,v));
}
inline int query(int l,int r,int k){
int ret=0;
It R=split(r+1),L=split(l);
for(It it=L;it!=R;++it)if(k==it->v)ret+=it->r-it->l+1;
return ret;
}
inline void update(int l,int r){
It R=split(r+1),L=split(l);
static int cnt[26],len;
for(ri i=0;i<26;++i)cnt[i]=0;
for(It it=L;it!=R;++it)cnt[it->v]+=it->r-it->l+1;
S.erase(L,R),len=0;
for(ri i=0;i<26;++i){
if(!cnt[i])continue;
S.insert(Node(l+len,l+len+cnt[i]-1,i)),len+=cnt[i];
}
}
const int N=5e4+5;
char s[N];
inline int getit(char x){return x>='A'&&x<='Z'?x-'A':x-'a';}
int main(){
n=read(),m=read(),scanf("%s",s+1);
for(ri i=1;i<=n;++i)S.insert(Node(i,i,getit(s[i])));
for(ri i=1,op,l,r,k;i<=m;++i){
op=read(),l=read(),r=read();
char t[2];
switch(op){
case 1:scanf("%s",t),cout<<query(l,r,getit(t[0]))<<'\n';break;
case 2:scanf("%s",t),assign(l,r,getit(t[0]));break;
default:update(l,r);
}
}
return 0;
}