CF610E Alphabet Permutations

题目传送门

分析:
对于两个相邻的字符\(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\n",ans);
		}
	}
}

posted @ 2020-07-06 17:05  Izayoi_Doyo  阅读(160)  评论(0编辑  收藏  举报