题意:n朵花,c种颜色,排成一排。共有m组询问,每个询问给出一个区间[l,r],输出[l,r]中满足个数大于等于2的颜色数
蛮经典的思想...
首先,我们用膝盖想出了可能用来维护区间的数据结构:树状数组、线段树
我们很容易想到一道智障题:输出[l,r]的花朵数,很显然,答案就是(r-l+1),也就是把每朵花赋值为1,维护一个傻乎乎的前缀和
当然,我们需要借鉴这种智障的思想
回归正题
我们先去掉那个讨厌的限制条件:个数大于等于2
那么就变成了一道简单题:输出[l,r]的颜色数
那么对于每一组[l,r],我们只需要把区间内的颜色第一次出现的位置赋值为1,其他位置均为0,然后用树状数组维护并用树状数组求出答案 find(r)-find(l-1)
加上这道题的限制呢?
很简单嘛,没有限制是“把区间内的颜色第一次出现的位置赋值为1,其他位置均为0”,那有了限制只需要“把区间内的颜色第二次出现的位置位置为1,其他位置均为0” 即可
我们用next[i]表示i位置的颜色下一次出现的位置
那么我们只需要保证i和next[i]都在[l,r]中且该颜色没有被数过,就可以在next[i]位置赋值为1
...很显然我们每次询问的清零工作是巨大的...
考虑离线
很显然这道题并没有要求强制在线,离线是可行的
预处理:所有颜色第二次出现的位置赋值为1,其他位置为0,此时维护的区间为[1,n]
由于我们已经有了前缀和的思想,那么
我们把m组[l,r]按左端点从小到大排序,然后让指针i从1~n扫一遍,且始终保证[i,n]中颜色第二次出现的位置为1,其他位置为0
当i移动到一组询问的左端点时,求出该组的答案 find(r)-find(l-1)
type
rec=record
l,r,p:longint;
end;
var
n,c,m,x :longint;
q :array[0..1000010] of rec;
last,next,t,fir :array[0..1000010] of longint;
ans :array[0..1000010] of longint;
i,j :longint;
procedure sort(ll,rr:longint);
var
x,i,j:longint;
y:rec;
begin
i:=ll;j:=rr;
x:=q[(ll+rr) div 2].l;
while (i<=j) do
begin
while (q[i].lx) do dec(j);
if (i<=j) then
begin
y:=q[i];q[i]:=q[j];q[j]:=y;
inc(i);dec(j);
end;
end;
if ill then sort(ll,j);
end;
procedure add(x,v:longint);
begin
while (x<=n) do
begin
inc(t[x],v);
inc(x,x and (-x));
end;
end;
function find(x:longint):Longint;
var
ans:longint;
begin
ans:=0;
while (x>0) do
begin
inc(ans,t[x]);
dec(x,x and (-x));
end;
exit(ans);
end;
begin
read(n,c,m);
for i:=1 to n do
begin
read(x);
if last[x]<>0 then next[last[x]]:=i else fir[x]:=i;
last[x]:=i;
end;
for i:=1 to m do
begin
read(q[i].l,q[i].r);
q[i].p:=i;
end;
sort(1,m);
//
for i:=1 to c do
if next[fir[i]]<>0 then add(next[fir[i]],1);
i:=1;
for j:=1 to m do
begin
while (i0 then add(next[i],-1);
if next[next[i]]<>0 then add(next[next[i]],1);
inc(i);
end;
ans[q[j].p]:=find(q[j].r)-find(q[j].l-1);
end;
for i:=1 to m do writeln(ans[i]);
end.
——by Eirlys