BZOJ2120:数颜色(分块版)
浅谈分块:https://www.cnblogs.com/AKMer/p/10369816.html
题目传送门:https://lydsy.com/JudgeOnline/problem.php?id=2120
树套树做法:https://www.cnblogs.com/AKMer/p/10189008.html
对于每个块维护块内\(pre\)上升的数组即可,每次查找直接\(lower\) \(bound\)一下,其它的跟树套树版的没什么区别。
时间复杂度:\(O(nlog\sqrt{n}+m\sqrt{n}log\sqrt{n})\)
空间复杂度:\(O(n)\)
代码如下:
#include <set>
#include <cmath>
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn=1e4+5,maxv=1e6+5;;
char s[5];
int n,m,block;
set<int>pos[maxv];
set<int>::iterator it;
int L[105],R[105],f[105][105];
int col[maxn],bel[maxn],pre[maxv],now[maxv];
int read() {
int x=0,f=1;char ch=getchar();
for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
for(;ch>='0'&&ch<='9';ch=getchar())x=x*10+ch-'0';
return x*f;
}
void build(int id) {
for(int i=L[id];i<=R[id];i++)
f[id][i-L[id]+1]=pre[i];
sort(f[id]+1,f[id]+R[id]-L[id]+2);
}
int main() {
n=read(),m=read(),block=sqrt(n);
for(int i=1;i<=n;i++) {
col[i]=read(),bel[i]=(i-1)/block+1;
if(pos[col[i]].empty())
pos[col[i]].insert(0),pos[col[i]].insert(n+1);
if(bel[i]!=bel[i-1])R[bel[i-1]]=i-1,L[bel[i]]=i;
pre[i]=now[col[i]],now[col[i]]=i,pos[col[i]].insert(i);
}R[bel[n]]=n;
for(int i=1;i<=bel[n];i++)build(i);
while(m--) {
scanf("%s",s+1);
if(s[1]=='Q') {
int ans=0,l=read(),r=read();
if(bel[l]==bel[r]) {
for(int i=l;i<=r;i++)if(pre[i]<l)ans++;
}
else {
for(int i=l;i<=R[bel[l]];i++)if(pre[i]<l)ans++;
for(int i=L[bel[r]];i<=r;i++)if(pre[i]<l)ans++;
for(int i=bel[l]+1;i<bel[r];i++)
ans+=lower_bound(f[i]+1,f[i]+R[i]-L[i]+2,l)-f[i]-1;
}
printf("%d\n",ans);
}
else {
int p=read(),c=read(),tmp=col[p],l,r;
if(pos[c].empty())pos[c].insert(0),pos[c].insert(n+1);
pos[tmp].erase(pos[tmp].find(p));
it=pos[tmp].lower_bound(p);
if(it!=pos[tmp].end()&&it!=pos[tmp].begin())
r=*it,it--,l=*it,pre[r]=l,build(bel[r]);
it=pos[c].lower_bound(p);
if(it!=pos[c].end())pre[*it]=p,build(bel[*it]);
if(it!=pos[c].begin())it--,pre[p]=*it,build(bel[p]);
pos[c].insert(p);
col[p]=c;
}
}
return 0;
}