[BZOJ2453]维护队列|分块

Description

你小时候玩过弹珠吗?
小朋友A有一些弹珠,A喜欢把它们排成队列,从左到右编号为1到N。为了整个队列鲜艳美观,小朋友想知道某一段连续弹珠中,不同颜色的弹珠有多少。当然,A有时候会依据个人喜好,替换队列中某个弹珠的颜色。但是A还没有学过编程,且觉得头脑风暴太浪费脑力了,所以向你来寻求帮助。
 
  思路很简单,在[l,r]中出现过的颜色数量
  我们设b[i]为第i位的数其之前出现的时候的下标
  只要统计在[l,r]间,b[i]<l的数量即可
  
  只要在每次修改的时候维护好b,再每次排序,询问的时候二分查找即可。
  如何维护b呢?
  对于每一个修改,它最多影响两个数的b,一个是它自身,一个是它后面遇到的第一个与它相等的数
  我们只要重新对两个块排序
  但是处理处b,刚开始我想要在每个块中再维护一个以颜色为第一关键字,下标为第二关键字的序列
  对于每个块二分找到修改的点前一次出现的位置 但是感觉十分麻烦
  看了下数据,修改的次数不会超过1000,n<=10000,每次重新算一遍b数组也是可以的
  但是第一次提交TLE了,发现习惯性的fillchar实际上时间并不那么快
  统计b的last数组实际上O(n)赋值比fillchar要快得多(因为last的范围的[0..10^6]啊/w\)
  1 program bzoj2453;
  2 const maxn=10010;maxm=1000010;
  3 var n,m,i,block,x,y,q:longint;
  4     ch:char;
  5     pos,a,b,pre:array[-1..maxn]of longint;
  6     last:array[-1..maxm]of longint;
  7 
  8 function min(a,b:longint):longint;
  9 begin
 10     if a<b then exit(a) else exit(b);
 11 end;
 12 
 13 procedure qsort(L,R:longint);
 14 var i,j,mid:longint;
 15 begin
 16     i:=L;j:=R;mid:=pre[random(R-L+1)+L];
 17     repeat
 18         while (i<R)and(pre[i]<mid) do inc(i);
 19         while (L<j)and(pre[j]>mid) do dec(j);
 20         if i<=j then
 21         begin
 22             pre[0]:=pre[i];pre[i]:=pre[j];pre[j]:=pre[0];
 23             inc(i);dec(j);
 24         end;
 25     until i>j;
 26     if i<R then qsort(i,R);
 27     if L<j then qsort(L,j);
 28 end;
 29 
 30 procedure new(x:longint);
 31 var l,r,i:longint;
 32 begin
 33     l:=(x-1)*block+1;r:=min(x*block,n);
 34     for i:=l to r do pre[i]:=b[i];
 35     qsort(l,r);
 36 end;
 37 
 38 procedure change(x,y:longint);
 39 var i:longint;
 40 begin
 41     for i:=1 to n do last[a[i]]:=0;
 42     a[x]:=y;
 43     for i:=1 to n do
 44     begin
 45         if last[a[i]]<>b[i] then
 46                 begin
 47 
 48                 b[i]:=last[a[i]];
 49                         new(pos[i]);
 50                 end;
 51         b[i]:=last[a[i]];
 52         last[a[i]]:=i;
 53     end;
 54 end;
 55 
 56 function find(x,y:longint):longint;
 57 var L,R,mid:longint;
 58 begin
 59     find:=(x-1)*block;
 60     L:=find+1;R:=min(x*block,n);
 61     while L<=R do
 62     begin
 63         mid:=(L+R) >> 1;
 64          if pre[mid]<=y then
 65         begin
 66             find:=mid;L:=mid+1;
 67         end else R:=mid-1;
 68     end;
 69     dec(find,(x-1)*block);
 70 end;
 71 
 72 procedure build;
 73 var i:longint;
 74 begin
 75     for i:=1 to n do last[a[i]]:=0;
 76     for i:=1 to n do
 77     begin
 78         b[i]:=last[a[i]];
 79         last[a[i]]:=i;
 80     end;
 81     for i:=1 to m do new(i);
 82 end;
 83 
 84 function query(x,y:longint):longint;
 85 var i,tot:longint;
 86 begin
 87     tot:=0;
 88     if pos[x]=pos[y] then
 89     begin
 90         for i:=x to y do if b[i]<=x-1 then inc(tot);
 91     end else
 92     begin
 93         for i:=x to pos[x]*block do if b[i]<=x-1 then inc(tot);
 94         for i:=(pos[y]-1)*block+1 to y do if b[i]<=x-1 then inc(tot);
 95         for i:=pos[x]+1 to pos[y]-1 do inc(tot,find(i,x-1));
 96     end;
 97     exit(tot);
 98 end;
 99 
100 begin
101     readln(n,q);
102     block:=trunc(sqrt(n));
103     for i:=1 to n do
104     begin
105         read(a[i]);
106         pos[i]:=(i-1) div block+1;
107     end;
108         readln;
109     if n mod block=0 then m:=n div block else m:=n div block+1;
110     build;
111     for i:=1 to q do
112     begin
113         read(ch);
114         if ch='Q' then
115         begin
116             readln(x,y);
117             writeln(query(x,y));
118         end else
119         begin
120             readln(x,y);
121             change(x,y);
122         end;
123     end;
124 end.

 

posted @ 2015-04-14 10:39  mjy0724  阅读(178)  评论(0编辑  收藏  举报