【题解】
用数组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; }