题目描述
NiroBC 姐姐奴役了一群跳蚤,并随时把它们丢到一台图灵机的纸带上。
一开始,纸带上没有跳蚤,每一个时刻,NiroBC 姐姐可能做以下三个操作之一:
- 在位置x 放置一只每次向右(坐标增大方向)跳t 格的跳蚤。
- 命令所有跳蚤向右跳跃一次,跳跃的距离为各自的t。
- 给定区间[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;
}