[线段树]跳蚤

Posted lllxq

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[线段树]跳蚤相关的知识,希望对你有一定的参考价值。

题目描述

NiroBC 姐姐奴役了一群跳蚤,并随时把它们丢到一台图灵机的纸带上。
一开始,纸带上没有跳蚤,每一个时刻,NiroBC 姐姐可能做以下三个操作之一:

  1. 在位置x 放置一只每次向右(坐标增大方向)跳t 格的跳蚤。
  2. 命令所有跳蚤向右跳跃一次,跳跃的距离为各自的t。
  3. 给定区间[l,r],求该区间内跳蚤的个数。

输入

第一行一个正整数Q,表示操作个数。
接下来Q 行,这Q 行中的第i 行含有若干个整数,描述第i 个操作。
(1) 若第一个整数为1,则紧跟两个整数xi 和ti,表示在xi 的位置放置一只每次向右跳跃ti 格的跳蚤。
(2) 若第一个整数为2,则命令所有跳蚤向右跳跃一次。
(3) 若第一个整数为3,则紧跟两个整数li 和ri,你需要输出此时区间[li, ri]中跳蚤的数量。

输出

对于每一个3 号操作,输出一行一个整数,表示该询问的答案。

题解

①对于步长大于200的跳蚤(大步长),最多跳500次就会出界,所以直接模拟,用树状数组维护跳蚤位置。复杂度O(500 * 1e5 * log(1e5))
②对于步长小于等于200的跳蚤(小步长),将这些跳蚤以步长t分类,对每个t建一颗线段树,以维护步长为t的跳蚤的起始位置。
比如某个操作1是要在x处插入一个步长位t的跳蚤,而在此之前已经实施了times次操作2(即跳跃),则可等价为这个操作1是在最开始实施(即所有操作之前)要在x-t * times处插入一个步长为t的跳蚤。这样将所有步长为t的跳蚤都统一跳了times步,而起始位置都做了相应的等价转换。
在查询[l,r]区间的小步长跳蚤个数时,若之前已实施times次操作2,则对步长t的跳蚤,相当于查询起始位置在[l-t * times,r-t * times]的跳蚤个数,并统计到答案里。复杂度O(200 * 1e5 * log(1e5))

代码

#include<bits/stdc++.h>
#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")
#define lowbit(x) x&(-x)
using namespace std;
const int n=1e5;

int c[100005];
inline void add(int pos,int val){
  for(int i=pos;i<=n;i+=lowbit(i)){
    c[i]+=val;
  }
}
inline int getsum(int pos){
  int ret=0;
  for(int i=pos;i>0;i-=lowbit(i)){
    ret+=c[i];
  }
  return ret;
}
pair<int,int> P[100005];
int tot=0;

int rt[205];
struct Node{
  int l,r,sum;
}node[3000005];
int ls[3000005],rs[3000005];
int id=0;
inline int newNode(int l,int r){
  node[++id]=Node{l,r,0};
  return id;
}

void addval_to_point(int k,int pos,int val){
  if(node[k].l==node[k].r){
    node[k].sum+=val;
    return;
  }
  int mid=(node[k].l+node[k].r)>>1;
  if(pos<=mid){
    if(!ls[k]) ls[k]=newNode(node[k].l,mid);
    addval_to_point(ls[k],pos,val);
  }
  else{
    if(!rs[k]) rs[k]=newNode(mid+1,node[k].r);
    addval_to_point(rs[k],pos,val);
  }
  node[k].sum=node[ls[k]].sum+node[rs[k]].sum;
}

int ask_interval_sum(int k,int l,int r){
  if(!k) return 0;
  if(l<=node[k].l&&node[k].r<=r) return node[k].sum;
  int mid=(node[k].l+node[k].r)>>1;
  int ret=0;
  if(l<=mid) ret+=ask_interval_sum(ls[k],l,r);
  if(r>mid) ret+=ask_interval_sum(rs[k],l,r);
  return ret;
}

int main()
{
    int q;scanf("%d",&q);
    int times=0;
    while(q--){
        int op;scanf("%d",&op);
        if(op==1){
            int x,t;scanf("%d%d",&x,&t);
            if(t>200){
                P[++tot]=make_pair(x,t);
                add(x,1);
            }
            else{
                int pos=x-t*times;
                if(!rt[t]) rt[t]=newNode(-t*100000,100000);
                addval_to_point(rt[t],pos,1);
            }
        }
        else if(op==2){
            times++;
            int _tot=0;
            for(int i=1;i<=tot;++i){
                int x=P[i].first,t=P[i].second;
                add(x,-1);
                if(x+t<=n){
                    add(x+t,1);
                    P[++_tot]=make_pair(x+t,t);
                }
            }
            tot=_tot;
        }
        else{
            int l,r;scanf("%d%d",&l,&r);
            int ans=getsum(r)-getsum(l-1);
            for(int i=1;i<=200;++i){
                ans+=ask_interval_sum(rt[i],l-i*times,r-i*times);
            }
            printf("%d
",ans);
        }
    }
    return 0;
}








以上是关于[线段树]跳蚤的主要内容,如果未能解决你的问题,请参考以下文章

CF725GMessages on a Tree 树链剖分+线段树

[BZOJ3065]带插入区间K小值

线段树详解

bzoj5000 OI树

BZOJ 5000: OI树

BZOJ 5000 OI树