【题解】
用数组pre[i]记录颜色i上一次出现的位置
则颜色C在{left,right}中第一次出现等价于:pre[C]<left
分块后,在每块中将pre值升序排列
先考虑查询:两头不完整的块内暴力,完整块之间二分查找小于left的pre值
在考虑修改:将第x个数颜色变为y,会影响到:
1. 原来pre[]==x的数的新pre值:在x之后的每个块中二分查找pre[]==x的值是否存在,若存在,就在这个块中暴力找到该位置
2. x及 x位置后第一个颜色为y 的新pre值:将color数组复制一份,并对其也在各个块内部排序,然后仿照上一种情况二分查找该颜色在块内是否存在
注意:若x后不存在颜色为y的位置,pre[x]应修改为last[y]表示颜色y最后一次出现的位置
之后将color,pre,last三个数组一起维护
【代码】
#include<stdio.h>
#include<stdlib.h>
#define SIZE 100
int block[10005],L[105],R[105],color[10005],num[10005],last[1000005],pre[10005],b[10005];
int n,cnt;
void jh(int* a,int* b)
{
int t=*a;
*a=*b;
*b=t;
}
void kp(int a[],int low,int high)
{
int i=low,j=high,mid=a[(i+j)/2];
while(i<j)
{
while(a[i]<mid) i++;
while(a[j]>mid) j--;
if(i<=j)
{
jh(&a[i],&a[j]);
i++;
j--;
}
}
if(j>low) kp(a,low,j);
if(i<high) kp(a,i,high);
}
int finddown(int a[],int N,int left,int right)//返回:a[left~right]中最后一个比N小的数的位置
{
int mid;
if(a[left]>=N) return left-1;
while(left<right)
{
mid=(left+right+1)/2;
if(a[mid]<N) left=mid;
else right=mid-1;
}
return left;
}
int find(int x)//返回[x,n]中第一个pre[i]==x的数i的位置
{
int i,j,t;
for(i=x;i<=R[block[x]];i++)
if(pre[i]==x) return i;
for(i=block[x]+1;i<=cnt;i++)
{
t=finddown(b,x,L[i],R[i]);
if(t<R[i]&&b[t+1]==x)
for(j=L[i];j<=R[i];j++)
if(pre[j]==x) return j;
}
return n+1;
}
int findnum(int x,int C)//返回x之后第一个color[i]==C的i的位置
{
int i,j,t;
if(x==n) return n+1;
for(i=x+1;i<=R[block[x]];i++)
if(color[i]==C) return i;
for(i=block[x]+1;i<=cnt;i++)
{
t=finddown(num,C,L[i],R[i]);
if(t<R[i]&&num[t+1]==C)
for(j=L[i];j<=R[i];j++)
if(color[j]==C) return j;
}
return n+1;
}
int query(int x,int y)
{
int ans=0,i;
if(block[x]+1>=block[y])
{
for(i=x;i<=y;i++)
if(pre[i]<x) ans++;
return ans;
}
for(i=x;i<=R[block[x]];i++)
if(pre[i]<x) ans++;
for(i=L[block[y]];i<=y;i++)
if(pre[i]<x) ans++;
for(i=block[x]+1;i<block[y];i++)
ans+=finddown(b,x,L[i],R[i])-L[i]+1;
return ans;
}
int main()
{
char opt;
int m,i,x,y,t;
scanf("%d%d",&n,&m);
cnt=(n-1)/SIZE+1;
for(i=1;i<=n;i++)
{
scanf("%d",&color[i]);
num[i]=color[i];
b[i]=pre[i]=last[color[i]];
last[color[i]]=i;
block[i]=(i-1)/SIZE+1;
}
for(i=1;i<=cnt;i++)
{
L[i]=(i-1)*SIZE+1;
R[i]=i*SIZE;
}
R[cnt]=n;
for(i=1;i<=cnt;i++)
{
kp(b,L[i],R[i]);
kp(num,L[i],R[i]);
}
for(;m>0;m--)
{
scanf("\n%c %d %d",&opt,&x,&y);
if(opt=='R')
{
if(color[x]==y) continue;//特判:替换前后颜色不变
t=find(x);
if(t<=n)
{
pre[t]=pre[x];
for(i=L[block[t]];i<=R[block[t]];i++)
b[i]=pre[i];
kp(b,L[block[t]],R[block[t]]);
}
else last[color[x]]=pre[x];
t=findnum(x,y);
if(t<=n)
{
pre[x]=pre[t];
pre[t]=x;
for(i=L[block[t]];i<=R[block[t]];i++)
b[i]=pre[i];
kp(b,L[block[t]],R[block[t]]);
for(i=L[block[x]];i<=R[block[x]];i++)
b[i]=pre[i];
kp(b,L[block[x]],R[block[x]]);
}
else
{
pre[x]=last[y];
last[y]=x;
for(i=L[block[x]];i<=R[block[x]];i++)
b[i]=pre[i];
kp(b,L[block[x]],R[block[x]]);
}
color[x]=y;
for(i=L[block[x]];i<=R[block[x]];i++)
num[i]=color[i];
kp(num,L[block[x]],R[block[x]]);
}
else printf("%d\n",query(x,y));
}
return 0;
}