https://www.luogu.org/problemnew/show/1903
用pre[i]数组记录上一次和当前i同色的位置
查询[l,r],若pre[i]<r,则说明在[l,i)区间内没用和i同色的,则++ans
于是就可以大胆地分块
对于每一块按照pre[i]排序,再进行二分了
复杂度O(qsqrt(n)log(n)).
#include<stdio.h> #include<math.h> #include<algorithm> #define FOR(i,s,t) for(register int i=s;i<=t;++i) using std::sort; using std::lower_bound; const int N=10011; const int M=1000011; inline int min(int a,int b){ return a<b?a:b; } int n,q,m,blo; int c[N],pos[N],pre[N],b[N],last[M]; inline int find(int x,int v){ int l=(x-1)*blo+1,r=min(x*blo,n); return lower_bound(pre+l,pre+r+1,v)-pre-l; } inline void reset(int x){ int l=(x-1)*blo+1,r=min(x*blo,n); FOR(i,l,r)pre[i]=b[i]; sort(pre+l,pre+r+1); } inline void build(){ FOR(i,1,n){ b[i]=last[c[i]]; last[c[i]]=i; pos[i]=(i-1)/blo+1; } FOR(i,1,m)reset(i); } inline int ask(int l,int r){ int ans=0; if(pos[l]==pos[r]){ FOR(i,l,r)if(b[i]<l)++ans; return ans; } for(register int i=l;i<=blo*pos[l];++i)if(b[i]<l)++ans; for(register int i=blo*(pos[r]-1)+1;i<=r;++i)if(b[i]<l)++ans; for(register int i=pos[l]+1;i<pos[r];++i)ans+=find(i,l); return ans; } inline void change(int x,int v){ FOR(i,1,n)last[c[i]]=0; c[x]=v; FOR(i,1,n){ int t=b[i]; b[i]=last[c[i]]; if(t!=b[i])reset(pos[i]); last[c[i]]=i; } } int main(){ scanf("%d%d",&n,&q); FOR(i,1,n)scanf("%d",c+i); blo=int(sqrt(n)); m=n%blo?(n/blo+1):n/blo; build(); char ch[5];int x,y; while(q--){ scanf("%s%d%d",ch,&x,&y); if(ch[0]==‘Q‘)printf("%d\n",ask(x,y)); else change(x,y); } return 0; }
时间: 2024-10-16 09:45:54