有n个数编号从0→n-1,两种操作:
Q L R:询问编号为L→R-1的数中共有多少种不同的数
M X Y:将编号为X的数改为Y
共有m个操作
此题,可以用高大上的树套树,亦可以用神奇的 JohnBer 大法。
当然,更可以用神奇的待修改的莫队算法(rzO莫队Orz)
这是本蒟蒻第一次接触莫队,顺便接触了带修莫队。
关于莫队算法,具体的网上很多都有,可以去找。
当然,对于莫队的实质,是Manhattan距离最小生成树。
分块的思路看看根据莫队的实质能不能优化,这应该是以后莫队优化的研究方向。
代码实现是有技巧的,可以把update和change写成函数。
当然,莫队的实现细节非常多,所以打起来一定要细心。
这个代码可以作为带修莫队的模板
#include<cstdio>
#include<cmath>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,b,a) for(int i=b;i>=a;i--)
using namespace std;
const int N=50010,M=1000010;
int n,m,ans,size,lyd[N],a[N],pos[N],val[N],c[M];
bool bz[N];
char ch;
struct Op
{
int l,r,k,pos;
}b[N];
struct modifyOp
{
int y,x,z;
}d[N];
bool cmp(Op a,Op b)
{
return pos[a.l]<pos[b.l] || pos[a.l]==pos[b.l] && pos[a.r]<pos[b.r]
|| pos[a.l]==pos[b.l] && pos[a.r]==pos[b.r] && a.k<b.k;
}
void update(int i)
{
if(!bz[i])
{
if(++c[a[i]]==1) ans++;
}
else
if(--c[a[i]]==0) ans--;
bz[i]^=1;
}
void change(int x,int y)
{
if(bz[x])
{
update(x);
a[x]=y;
update(x);
}
else a[x]=y;
}
int main()
{
freopen("len.in","r",stdin);
freopen("len.out","w",stdout);
int _,l,r,k;
scanf("%d %d",&n,&_);
size=ceil(pow(n,1.0/3));
size*=size;
fo(i,1,n) scanf("%d",&lyd[i]),a[i]=lyd[i],pos[i]=(i-1)/size;
scanf("\n");
int m=0,num=0;
fo(i,1,_)
{
scanf("%c %d %d\n",&ch,&l,&r),l++;
if(ch=='Q') b[++m].l=l,b[m].r=r,b[m].pos=m,b[m].k=num;
else d[++num].y=lyd[l],d[num].x=l,d[num].z=r,lyd[l]=r;
}
sort(b+1,b+m+1,cmp);
fo(i,1,b[1].k) a[d[i].x]=d[i].z;
fo(i,b[1].l,b[1].r) update(i);
val[b[1].pos]=ans;
l=b[1].l,r=b[1].r,k=b[1].k;
fo(i,2,m)
{
if(k<b[i].k)
fo(j,k+1,b[i].k) change(d[j].x,d[j].z);
else
fd(j,k,b[i].k+1) change(d[j].x,d[j].y);
if(l<b[i].l)
fo(j,l,b[i].l-1) update(j);
else
fo(j,b[i].l,l-1) update(j);
if(r<b[i].r)
fo(j,r+1,b[i].r) update(j);
else
fo(j,b[i].r+1,r) update(j);
l=b[i].l,r=b[i].r,k=b[i].k;
val[b[i].pos]=ans;
}
fo(i,1,m) printf("%d\n",val[i]);
return 0;
}