2016-05-28 11:20:22
共同的思路:
维护某种颜色上一次在哪里出现pre,可以知道当pre<询问的l时更新答案
块内按照pre排序
修改的时候重新O(n)扫一遍,如果和之前的不一样,则重置所在块
查询时和普通分块一样,整块二分,两边暴力
BZOJ2453 http://www.lydsy.com/JudgeOnline/problem.php?id=2453
BZOJ2120 http://www.lydsy.com/JudgeOnline/problem.php?id=2120
#include<bits/stdc++.h> #define inf 1000000000 #define ll long long #define N 10005 #define M 1000005 using namespace std; int read(){ int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } int block,n,m,q; int a[N],b[N],c[N],pre[M],pos[N]; char ch[2]; void reset(int x){ int l=(x-1)*block+1,r=min(x*block,n); for(int i=l;i<=r;i++)b[i]=a[i]; sort(b+l,b+r+1); } void update(int x,int v){ for(int i=1;i<=n;i++)pre[c[i]]=0; c[x]=v; for(int i=1;i<=n;i++){ int t=a[i]; a[i]=pre[c[i]]; if(t!=a[i])reset(pos[i]); pre[c[i]]=i; } } int find(int x,int v){ int l=(x-1)*block+1,r=x*block,mid,tmp; while(l<=r){ mid=l+r>>1; if(b[mid]<v)tmp=mid,l=mid+1; else r=mid-1; } return tmp-(x-1)*block; } int query(int l,int r){ int ans=0; if(pos[l]==pos[r]){ for(int i=l;i<=r;i++)if(a[i]<l)ans++; } else{ for(int i=l;i<=block*pos[l];i++)if(a[i]<l)ans++; for(int i=(pos[r]-1)*block+1;i<=r;i++)if(a[i]<l)ans++; } for(int i=pos[l]+1;i<pos[r];i++)ans+=find(i,l); return ans; } int main(){ n=read();q=read(); for(int i=1;i<=n;i++){ c[i]=read();a[i]=pre[c[i]]; pre[c[i]]=i; } block=(int)sqrt(n); for(int i=1;i<=n;i++)pos[i]=(i-1)/block+1; if(n%block)m=n/block+1;else m=n/block; for(int i=1;i<=m;i++)reset(i); while(q--){ scanf("%s",ch); int x=read(),y=read(); if(ch[0]=='Q')printf("%d\n",query(x,y)); else update(x,y); } return 0; }