bzoj 2743 树状数组+离线处理

来源:互联网 发布:知乎如何删除关注话题 编辑:程序博客网 时间:2024/06/11 18:37

题意: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].l<x) do inc(i);      while (q[j].l>x) 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 i<rr then sort(i,rr);   if j>ll 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 (i<q[j].l) do      begin         if next[i]<>0 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


0 0
原创粉丝点击