bzoj 2453 && bzoj 2120 分块

61 篇文章 0 订阅
1 篇文章 0 订阅

题意:给定一个序列,支持两种操作:(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


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值