题意:给定一个序列,支持两种操作:(1)多次询问某个区间所含数字种类数 (2)单点修改
强行分块...
对每个位置维护一个pre数组,表示当前位置颜色的上一个位置
如果pre[i]<l则这个颜色是第一次在这个区间内出现,答案+1
每个块内对pre数组排序,
询问时,在整块里每次二分找到l的严格下界,加到答案里;两边的散块暴力找
对于修改就把影响到的块暴力修改即可..
uses math;
var
n,m,q,x,y :longint;
i :longint;
ch :char;
last :array[0..1000010] of longint;
pre,a,bloc :array[0..10010] of longint;
num :array[0..110,0..110] of longint;
flag :array[0..110] of boolean;
procedure sort(x,l,r:longint);
var
i,j,y,z:longint;
begin
i:=l; j:=r; y:=num[x,(l+r)>>1];
while (i<=j) do
begin
while num[x,i]<y do inc(i);
while num[x,j]>y do dec(j);
if i<=j then
begin
z:=num[x,i]; num[x,i]:=num[x,j]; num[x,j]:=z;
inc(i); dec(j);
end;
end;
if i<r then sort(x,i,r);
if j>l then sort(x,l,j);
end;
function work(x,y:longint):longint;
var
l,r,mid:longint;
begin
l:=1; r:=num[x,0];
if num[x,1]>=y then exit(0);
mid:=(l+r+1)>>1;
while (l<r) do
begin
if num[x,mid]<y then l:=mid else r:=mid-1;
mid:=(l+r+1)>>1;
end;
exit(mid);
end;
function find(a,b:longint):longint;
var
ans:longint;
i:longint;
begin
ans:=0;
for i:=a to min(bloc[a]*m,b) do
if pre[i]<a then inc(ans);
if bloc[a]<>bloc[b] then
for i:=m*(bloc[b]-1)+1 to b do
if pre[i]<a then inc(ans);
for i:=bloc[a]+1 to bloc[b]-1 do inc(ans,work(i,a));
exit(ans);
end;
procedure reset(x:longint);
var
i:longint;
begin
num[x,0]:=0;
for i:=m*(x-1)+1 to min(n,m*x) do
begin
inc(num[x,0]);
num[x,num[x,0]]:=pre[i];
end;
sort(x,1,num[x,0]);
end;
procedure change(x,y:longint);
var
i:longint;
tt:longint;
begin
for i:=1 to n do last[a[i]]:=0;
for i:=1 to bloc[n] do flag[i]:=false;
a[x]:=y;
for i:=1 to n do
begin
tt:=last[a[i]];
if tt<>pre[i] then flag[bloc[i]]:=true;
pre[i]:=last[a[i]];
last[a[i]]:=i;
end;
for i:=1 to bloc[n] do
if flag[i] then reset(i);
end;
begin
read(n,q); m:=trunc(sqrt(n));
for i:=1 to n do
begin
read(a[i]);
pre[i]:=last[a[i]];
last[a[i]]:=i;
bloc[i]:=(i-1) div m+1;
end;
for i:=1 to bloc[n] do reset(i);
//
readln;
for i:=1 to q do
begin
read(ch);
readln(x,y);
if ch='Q' then writeln(find(x,y)) else change(x,y);
end;
end.
——by Eirlys