bzoj 2743 树状数组+离线处理

61 篇文章 0 订阅
19 篇文章 0 订阅

题意: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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值