题目传送门
分析:
对于两个相邻的字符(a,b),在排列中的位置为(rk_a,rk_b),如果(rk_a>=rk_b)那么必须多用一个模式串
由于字符集很小,我们开一个线段树,每个节点上有一个(K*K)的数组,(a[i][j])表示某一段区间前一个为(i)后一个为(j)的个数
维护两端字符可以简单合并,区间修改也可以维护
复杂度(O(qlognK^2))
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<algorithm>
#include<queue>
#include<map>
#include<set>
#define maxn 200005
#define INF 0x3f3f3f3f
using namespace std;
inline int getint()
{
int num=0,flag=1;char c;
while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;
while(c>='0'&&c<='9')num=num*10+c-48,c=getchar();
return num*flag;
}
int n,m,K;
int a[maxn<<2][10][10],L[maxn<<2],R[maxn<<2],len[maxn<<2],lz[maxn<<2];
char s[maxn];
int rk[10];
inline void update(int i,int x)
{memset(a[i],0,sizeof a[i]);a[i][x][x]=len[i]-1,L[i]=R[i]=lz[i]=x;}
inline void pushdown(int i)
{if(~lz[i])update(i<<1,lz[i]),update(i<<1|1,lz[i]),lz[i]=-1;}
inline void pushup(int i)
{
int l=i<<1,r=i<<1|1;
for(int j=0;j<K;j++)for(int k=0;k<K;k++)a[i][j][k]=a[l][j][k]+a[r][j][k];
a[i][R[l]][L[r]]++,L[i]=L[l],R[i]=R[r];
}
inline void build(int i,int l,int r)
{
len[i]=r-l+1,lz[i]=-1;
if(l==r){L[i]=R[i]=s[l]-97;return;}
int mid=(l+r)>>1;
build(i<<1,l,mid),build(i<<1|1,mid+1,r);
pushup(i);
}
inline void update(int i,int l,int r,int ql,int qr,int x)
{
if(r<ql||qr<l)return;
if(ql<=l&&r<=qr){update(i,x);return;}
pushdown(i);
int mid=(l+r)>>1;
update(i<<1,l,mid,ql,qr,x),update(i<<1|1,mid+1,r,ql,qr,x);
pushup(i);
}
int main()
{
n=getint(),m=getint(),K=getint();
scanf("%s",s+1);
build(1,1,n);
while(m--)
{
int op=getint();
if(op==1)
{
int l=getint(),r=getint();
scanf("%s",s+1);
update(1,1,n,l,r,s[1]-97);
}
else
{
int ans=1;
scanf("%s",s+1);
for(int i=1;i<=K;i++)rk[s[i]-97]=i;
for(int i=0;i<K;i++)for(int j=0;j<K;j++)if(rk[i]>=rk[j])ans+=a[1][i][j];
printf("%d
",ans);
}
}
}