题意:有n个位置(n<=10000),初始每个位置有一种颜色,要求资磁两个操作:
R x y 把位置x的颜色变为y
Q x y 求x到y这个区间内出现了多少种不同的颜色
分析:让我们来介绍一下带修改莫队,其实就是三维莫队。
每个询问本来只有二元组(l,r)表示,现在我用一个三元组(l,r,x)表示,意思是对[l,r]进行询问,x表示在此次询问操作之前经过了x次修改操作。
然后和普通莫队一样,第三维也是暴力移动的。即知道(l,r,x)的答案可以知道(l-1,r,x)(l+1,r,x)(l,r-1,x)(l,r+1,x)(l,r,x-1)(l,r,x+1)的答案。
按l所在的块为第一关键字,r所在的块为第二关键字,x为第三关键字排序处理。
块大小需要设为n^2/3,总复杂度是n^5/3,这里就略去复杂度的证明。
代码:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define B 250
#define N 10005
using namespace std;
int n,m,q,scha,color[N],t[N*100],tot;
struct data{int l,r,id,ans,x;}w[N];
struct chan{int x,y;}cha[N];
bool cmp(data a,data b)
{
return (a.l/B<b.l/B||a.l/B==b.l/B&&a.r/B<b.r/B||a.l/B==b.l/B&&a.r/B<b.r/B&&a.x<b.x);
}
void updata(int x,int y)
{
if (y==1)
{
if (!t[color[x]]) tot++;
t[color[x]]++;
}else
{
t[color[x]]--;
if (!t[color[x]]) tot--;
}
}
void change(int x,int y,int l,int r)
{
if (cha[x].x>=l&&cha[x].x<=r) updata(cha[x].x,-1);
swap(color[cha[x].x],cha[x].y);
if (cha[x].x>=l&&cha[x].x<=r) updata(cha[x].x,1);
}
void solve()
{
updata(1,1);
for (int i=1,l=1,r=1,x=0;i<=q;i++)
{
for (;r<w[i].r;r++)
updata(r+1,1);
for (;r>w[i].r;r--)
updata(r,-1);
for (;l<w[i].l;l++)
updata(l,-1);
for (;l>w[i].l;l--)
updata(l-1,1);
for (;x<w[i].x;x++)
change(x+1,1,l,r);
for (;x>w[i].x;x--)
change(x,-1,l,r);
w[i].ans=tot;
}
}
bool cmp1(data a,data b)
{
return a.id<b.id;
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
scanf("%d",&color[i]);
for (int i=1;i<=m;i++)
{
char ch[2];
int x,y;
scanf("%s%d%d",ch,&x,&y);
if (ch[0]=='R')
{
cha[++scha].x=x;
cha[scha].y=y;
}else
{
w[++q].l=x;
w[q].r=y;
w[q].x=scha;
w[q].id=q;
}
}
sort(w+1,w+q+1,cmp);
solve();
sort(w+1,w+q+1,cmp1);
for (int i=1;i<=q;i++)
printf("%d\n",w[i].ans);
return 0;
}