题目:http://codeforces.com/contest/610/problem/E
题意:给出一个长为n,n<=200000只含前k,k<=10个字母的ss,有m,m<=2w次操作,每次:
1 l r c 将[l,r]范围内的字符都改为c
2 s保证s是前k个字母的一个排列,询问最少将s循环都少次使得ss是s的子序列
分析:考虑询问,令dp[i]代表ss中前i个字符的答案,显然有
代码:
#include
using namespace std;
#define ls l,mid,x<<1
#define rs mid+1,r,x<<1|1
const int Maxn=200020;
int n,m,k;
char s[Maxn];
int a[Maxn<<2];
int rep[22][22],rk[22];
void push_up(int x)
{
if(a[x<<1]==a[x<<1|1])a[x]=a[x<<1];
else a[x]=-1;
}
void push_down(int x)
{
if(a[x]!=-1)
{
a[x<<1]=a[x<<1|1]=a[x];
}
}
void build(int l,int r,int x)
{
if(l==r){a[x]=s[l]-'a';return;}
a[x]=-1;
int mid=(l+r)>>1;
build(ls);build(rs);
push_up(x);
}
int query(int tar,int l,int r,int x)
{
if(l==r||a[x]!=-1)return a[x];
int mid=(l+r)>>1;
if(tar<=mid)return query(tar,ls);
return query(tar,rs);
}
int tpc;//前一个字符,应该初始化为>10
void ask(int L,int R,int l,int r,int x)//注意边界
{
int mid=(l+r)>>1;
if(L<=l&&R>=r)
{
if(a[x]!=-1){rep[tpc][a[x]]--;rep[a[x]][a[x]]-=r-l;tpc=a[x];}
else{ask(L,R,ls);ask(L,R,rs);}
return;
}
push_down(x);
if(L<=mid)ask(L,R,ls);
if(R>mid)ask(L,R,rs);
}
void add(int L,int R,int w,int l,int r,int x)
{
if(L<=l&&R>=r)
{
a[x]=w;
return;
}
push_down(x);
int mid=(l+r)>>1;
if(L<=mid)add(L,R,w,ls);
if(R>mid)add(L,R,w,rs);
push_up(x);
}
int main()
{
scanf("%d%d%d",&n,&m,&k);
scanf("%s",s+1);
for(int i=2;i<=n;i++)rep[s[i-1]-'a'][s[i]-'a']++;
build(1,n,1);
while(m--)
{
int ty,l,r;
char ss[22];
scanf("%d",&ty);
if(ty==1)
{
scanf("%d%d%s",&l,&r,ss);
int c=ss[0]-'a';
tpc=20;
ask(max(l-1,1),min(n,r+1),1,n,1);
add(l,r,c,1,n,1);
if(l>1){int t1=query(l-1,1,n,1);rep[t1][c]++;}
if(rint t1=query(r+1,1,n,1);rep[c][t1]++;}
rep[c][c]+=r-l;
}
else
{
scanf("%s",ss);
for(int i=0;i'a']=i;
int ans=1;
for(int i=0;ifor(int j=0;jif(rk[i]>=rk[j])ans+=rep[i][j];
}
printf("%d\n",ans);
}
}
}