题面

H

a

n

d

I

n

D

e

v

i

l

\rm HandInDevil

HandInDevil 的头发很油,因此随时有跳蚤跳上

H

a

n

d

I

n

D

e

v

i

l

\rm HandInDevil

HandInDevil 的头发。

现在把

H

a

n

d

I

n

D

e

v

i

l

\rm HandInDevil

HandInDevil 的头发抽象成一个数轴,一开始上面没有跳蚤。每一个时刻,有三种事件之一会发生:

  1. 有一个每次会跳

    t

    t

    t 格的跳蚤跳上

    H

    a

    n

    d

    I

    n

    D

    e

    v

    i

    l

    \rm HandInDevil

    HandInDevil 的头发的位置

    x

    x

    x 。

  2. H

    a

    n

    d

    I

    n

    D

    e

    v

    i

    l

    \rm HandInDevil

    HandInDevil 开始挠头发,每一只跳蚤向右跳一次,跳的距离为各自的

    t

    t

    t 。

  3. H

    a

    n

    d

    I

    n

    D

    e

    v

    i

    l

    \rm HandInDevil

    HandInDevil 没有挠到跳蚤,打算找下一个目标,向你询问 头发上区间

    [

    l

    ,

    r

    ]

    [l,r]

    [l,r] 内跳蚤的个数。

输入

第一行一个整数

Q

Q

Q,表示事件个数。

接下来

Q

Q

Q 行描述事件,输入若干个整数:

  • 若第一个数为 1 ,则接下来跟着两个整数

    x

    i

    x_i

    xi​ 和

    t

    i

    t_i

    ti​ ,表示一个跳蚤跳了上去。

  • 若第一个数为 2 ,则

    H

    a

    n

    d

    I

    n

    D

    e

    v

    i

    l

    \rm HandInDevil

    HandInDevil 挠了一次头发,所有跳蚤往右跳。

  • 若第一个数为 3 ,则接下来跟着两个整数

    l

    i

    l_i

    li​ 和

    r

    i

    r_i

    ri​ ,你需要输出此时区间

    [

    l

    i

    ,

    r

    i

    ]

    [l_i,r_i]

    [li​,ri​] 中跳蚤的数量。

输出

对于每一个事件 3 ,输出一行一个整数,表示询问的答案。

Sample Input

10
1 4 1
1 2 2
3 3 5
2
3 3 5
2
3 3 5
3 6 6
1 5 1
3 3 5

Sample Output

1
2
0
2
1

数据范围

1

Q

,

x

i

,

t

i

,

l

i

,

r

i

1

0

5

  

,
  

l

i

r

i

1\leq Q,x_i,t_i,l_i,r_i\leq 10^5\;,\;l_i\leq r_i

1≤Q,xi​,ti​,li​,ri​≤105,li​≤ri​

2 ms , 512 mb

\text{2 ms , 512 mb}

2 ms , 512 mb

题解

-既然跟 HandInDevil 扯上关系那肯定是分块题啊-

考虑分块,令块大小为

B

B

B,头发有效长度为

n

n

n,我们把

t

i

>

B

t_i> B

ti​>B 的跳蚤暴力跳,然后用线段树或树状数组等数据结构维护区间和,这部分复杂度为

O

(

Q

n

B

log

n

)

O(Q\frac{n}{B}\log n)

O(QBn​logn) 。

然后对于

t

i

B

t_i\leq B

ti​≤B 的跳蚤,我们建立

B

B

B 棵平衡树,假设跳蚤

i

i

i 跳上时已经发生了

c

n

t

i

cnt_i

cnti​ 次事件 2,那么我们就把

x

i

c

n

t

i

t

i

x_i-cnt_i*t_i

xi​−cnti​∗ti​ 加进第

t

i

t_i

ti​ 棵平衡树里。

询问的时候,可以先加上区间内

t

i

>

B

t_i>B

ti​>B 的跳蚤数量,然后在每棵平衡树里进行询问。假设此时事件 2 发生了

c

n

t

cnt'

cnt′ 次,对于第

i

i

i 棵平衡树,先把

[

l

i

,

r

i

]

[l_i,r_i]

[li​,ri​] 变成

[

l

i

c

n

t

i

,

r

i

c

n

t

i

]

[l_i-cnt'*i,r_i-cnt'*i]

[li​−cnt′∗i,ri​−cnt′∗i] ,然后在该平衡树里询问值在这个区间内的结点数量,加进答案。这部分复杂度是

O

(

Q

B

log

Q

)

O(QB\log Q)

O(QBlogQ) 。

由于

Q

Q

Q 和

n

n

n 同阶,所以取

B

=

n

B=\sqrt{n}

B=n

​ 时复杂度最小,总复杂度为

O

(

Q

n

log

n

)

O(Q\sqrt n\log n)

O(Qn

​logn) ,险过。

CODE

#include<set>
#include<map>
#include<cmath>
#include<ctime>
#include<stack>
#include<queue>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define MAXN 100005
#define DB double
#define LL long long
#define ENDL putchar('\n')
#define lowbit(x) (-(x) & (x))
#define FI first
#define SE second
#pragma GCC optimize(2)
LL read() {
LL f = 1,x = 0;char s = getchar();
while(s < '0' || s > '9') {if(s=='-')f = -f;s = getchar();}
while(s >= '0' && s <= '9') {x=x*10+(s-'0');s = getchar();}
return f * x;
}
const int MOD = 998244353;
const int SQ = 317;
int n = 100000,m,i,j,s,o,k;
struct np{int s[2];np(){s[0]=s[1]=0;}np(int A,int B){s[0]=A;s[1]=B;}};
struct tr{
int s[2];
LL ky;int hp,siz;
tr(){s[0]=s[1]=ky=hp=siz=0;}
}tre[MAXN];
int buc[MAXN],cntb;
int newnode(LL key) {
int x = buc[cntb --]; tre[x] = tr();
tre[x].ky = key; tre[x].hp = rand()*1ll*rand()%MOD;
tre[x].siz = 1; return x;
}
int update(int x) {
if(!x) return x;
int ls = tre[x].s[0],rs = tre[x].s[1];
tre[x].siz = tre[ls].siz + tre[rs].siz + 1;
return x;
}
np spli(int x,LL key) {
np as(0,0); if(!x) return as;
int d = (tre[x].ky <= key);
as = spli(tre[x].s[d],key);
tre[x].s[d] = as.s[d^1];
as.s[d^1] = update(x);
return as;
}
np spli2(int x,int kth) {
np as(0,0); if(!x) return as;
int d = (tre[tre[x].s[0]].siz+1 <= kth);
if(d) kth -= tre[tre[x].s[0]].siz+1;
as = spli(tre[x].s[d],kth);
tre[x].s[d] = as.s[d^1];
as.s[d^1] = update(x);
return as;
}
int merg(int p1,int p2) {
if(!p1 || !p2) return p1+p2;
if(tre[p1].hp < tre[p2].hp) {
tre[p1].s[1] = merg(tre[p1].s[1],p2);
return update(p1);
}
tre[p2].s[0] = merg(p1,tre[p2].s[0]);
return update(p2);
}
int ins(int x,LL key) {
np p = spli(x,key);
return merg(p.s[0],merg(newnode(key),p.s[1]));
}
int del(int x,LL key) {
np pl = spli(x,key-1);
np pr = spli2(pl.s[1],1);
if(pr.s[0] && tre[pr.s[0]].ky == key) buc[++ cntb] = pr.s[0];
else pr.s[1] = merg(pr.s[0],pr.s[1]);
return merg(pl.s[0],pr.s[1]);
}
int query(int &x,LL l,LL r) {
if(l > r) return 0;
np p1 = spli(x,l-1);
np p2 = spli(p1.s[1],r);
int as = tre[p2.s[0]].siz;
x = merg(p1.s[0],merg(p2.s[0],p2.s[1]));
return as;
}
int bl[SQ+5],li[SQ+5],rt[SQ+5];
int c[MAXN];
void addt(int x,int y){while(x<=n)c[x]+=y,x+=lowbit(x);}
int sum(int x) {int as=0;while(x>0)as+=c[x],x-=lowbit(x);return as;}
pair<int,int> PAIR(int A,int B) {pair<int,int> a;a.FI=A;a.SE=B;return a;}
pair<int,int> q1[MAXN];
int cnq;
int main() {
freopen("flea.in","r",stdin);
freopen("flea.out","w",stdout);
m = 0;
for(int i = 100000;i > 0;i --) {
buc[++ cntb] = i;
int bid = (i+SQ-1)/SQ;
m = max(m,bid); li[bid] = i;
}
li[m+1] = 100001;
srand(time(0));
int Q = read(),cnt2 = 0;
while(Q --) {
k = read();
if(k == 1) {
s = read();o = read();
if(o > SQ) q1[++ cnq] = PAIR(s,o),addt(s,1);
else {
LL ad = (LL)s-cnt2*1ll*o;
rt[o] = ins(rt[o],ad);
}
}
else if(k == 2) {
cnt2 ++;
int leq = cnq;cnq = 0;
for(int i = 1;i <= leq;i ++) {
int ss = q1[i].FI,tt = q1[i].SE;
addt(ss,-1);
ss += tt;
if(ss <= 100000) {
addt(ss,1);
q1[++ cnq] = PAIR(ss,tt);
}
}
}
else {
s = read();o = read();
int ans = sum(o)-sum(s-1);
for(int i = 1;i <= SQ;i ++) {
LL ad1 = (LL)s-cnt2*1ll*i , ad2 = (LL)o-cnt2*1ll*i;
ans += query(rt[i],ad1,ad2);
}
printf("%d\n",ans);
}
}
return 0;
}

Better Solution

有一个更快的做法,我们把

t

i

>

B

t_i>B

ti​>B 的跳蚤暴力跳时,用分块维护区间和。把头发序列分块,每个块大小为

B

B

B,修改

O

(

1

)

O(1)

O(1) ,询问

O

(

n

B

)

O(\frac{n}{B})

O(Bn​),这部分修改总复杂度

O

(

Q

n

B

)

O(Q\frac{n}{B})

O(QBn​),询问总复杂度

O

(

Q

n

B

)

O(Q\frac{n}{B})

O(QBn​) 。

那么取块大小为

B

=

n

log

n

B=\sqrt{\frac{n}{\log n}}

B=lognn​

​ ,总复杂度就为

O

(

Q

n

log

n

)

O(Q\sqrt{n\log n})

O(Qnlogn

​),实际块大小还可以根据平衡树的常数微调,比之前的做法快上不少。

CODE

#include<set>
#include<map>
#include<cmath>
#include<ctime>
#include<stack>
#include<queue>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define MAXN 100005
#define DB double
#define LL long long
#define ENDL putchar('\n')
#define lowbit(x) (-(x) & (x))
#define FI first
#define SE second
#pragma GCC optimize(2)
LL read() {
LL f = 1,x = 0;char s = getchar();
while(s < '0' || s > '9') {if(s=='-')f = -f;s = getchar();}
while(s >= '0' && s <= '9') {x=x*10+(s-'0');s = getchar();}
return f * x;
}
const int MOD = 998244353;
const int SQ = 77;
int n = 100000,m,i,j,s,o,k;
struct np{int s[2];np(){s[0]=s[1]=0;}np(int A,int B){s[0]=A;s[1]=B;}};
struct tr{
int s[2];
LL ky;int hp,siz;
tr(){s[0]=s[1]=ky=hp=siz=0;}
}tre[MAXN];
int buc[MAXN],cntb;
int newnode(LL key) {
int x = buc[cntb --]; tre[x] = tr();
tre[x].ky = key; tre[x].hp = rand()*1ll*rand()%MOD;
tre[x].siz = 1; return x;
}
int update(int x) {
if(!x) return x;
int ls = tre[x].s[0],rs = tre[x].s[1];
tre[x].siz = tre[ls].siz + tre[rs].siz + 1;
return x;
}
np spli(int x,LL key) {
np as(0,0); if(!x) return as;
int d = (tre[x].ky <= key);
as = spli(tre[x].s[d],key);
tre[x].s[d] = as.s[d^1];
as.s[d^1] = update(x);
return as;
}
np spli2(int x,int kth) {
np as(0,0); if(!x) return as;
int d = (tre[tre[x].s[0]].siz+1 <= kth);
if(d) kth -= tre[tre[x].s[0]].siz+1;
as = spli(tre[x].s[d],kth);
tre[x].s[d] = as.s[d^1];
as.s[d^1] = update(x);
return as;
}
int merg(int p1,int p2) {
if(!p1 || !p2) return p1+p2;
if(tre[p1].hp < tre[p2].hp) {
tre[p1].s[1] = merg(tre[p1].s[1],p2);
return update(p1);
}
tre[p2].s[0] = merg(p1,tre[p2].s[0]);
return update(p2);
}
int ins(int x,LL key) {
np p = spli(x,key);
return merg(p.s[0],merg(newnode(key),p.s[1]));
}
int del(int x,LL key) {
np pl = spli(x,key-1);
np pr = spli2(pl.s[1],1);
if(pr.s[0] && tre[pr.s[0]].ky == key) buc[++ cntb] = pr.s[0];
else pr.s[1] = merg(pr.s[0],pr.s[1]);
return merg(pl.s[0],pr.s[1]);
}
int query(int &x,LL l,LL r) {
if(l > r) return 0;
np p1 = spli(x,l-1);
np p2 = spli(p1.s[1],r);
int as = tre[p2.s[0]].siz;
x = merg(p1.s[0],merg(p2.s[0],p2.s[1]));
return as;
}
int bl[MAXN],li[MAXN],rt[MAXN];
int ct[MAXN];
pair<int,int> PAIR(int A,int B) {pair<int,int> a;a.FI=A;a.SE=B;return a;}
pair<int,int> q1[MAXN];
int cnq;
int main() {
freopen("flea.in","r",stdin);
freopen("flea.out","w",stdout);
m = 0;
for(int i = 100000;i > 0;i --) {
buc[++ cntb] = i;
int bid = (i+SQ-1)/SQ;
m = max(m,bid); li[bid] = i;
}
li[m+1] = 100001;
srand(time(0));
int Q = read(),cnt2 = 0;
while(Q --) {
k = read();
if(k == 1) {
s = read();o = read();
if(o > SQ) q1[++ cnq] = PAIR(s,o),ct[s] ++,bl[(s+SQ-1)/SQ] ++;
else {
LL ad = (LL)s-cnt2*1ll*o;
rt[o] = ins(rt[o],ad);
}
}
else if(k == 2) {
cnt2 ++;
int leq = cnq;cnq = 0;
for(int i = 1;i <= leq;i ++) {
int ss = q1[i].FI,tt = q1[i].SE;
ct[ss] --; bl[(ss+SQ-1)/SQ] --;
ss += tt;
if(ss <= 100000) {
ct[ss] ++; bl[(ss+SQ-1)/SQ] ++;
q1[++ cnq] = PAIR(ss,tt);
}
}
}
else {
s = read();o = read();
int ans = 0;
int ll = (s+SQ-1)/SQ,rr = (o+SQ-1)/SQ;
for(int i = ll+1;i <= rr-1;i ++) ans += bl[i];
if(ll == rr) for(int i = s;i <= o;i ++) ans += ct[i];
else {
for(int i = s;(i+SQ-1)/SQ == ll;i ++) ans += ct[i];
for(int i = o;(i+SQ-1)/SQ == rr;i --) ans += ct[i];
}
for(int i = 1;i <= SQ;i ++) {
LL ad1 = (LL)s-cnt2*1ll*i , ad2 = (LL)o-cnt2*1ll*i;
ans += query(rt[i],ad1,ad2);
}
printf("%d\n",ans);
}
}
return 0;
}

HandInDevil 的头发 (分 块)的更多相关文章

  1. 列表 ul ol dl 和 块级标签和行及标签之间的转换

    1. 无序列表 有序列表 自定义列表 1,无序列表 第一 你不必须有子标签  <li></li> 第二 ul天生自带内外边距 List-style的属性值 circle(空心圆 ...

  2. js瀑布流 原理实现揭秘 javascript 原生实现

    web,js瀑布流揭秘 瀑布流再很久之前流行,可能如我一样入行晚的 ,可能就没有机会去使用.但是这个技术终究是个挺炫酷的东西,花了一个上午来研究,用原生js实现了一个,下面会附上源码,供大家解读. 说 ...

  3. ASP.NET 文件下载

    using System; using System.Web; using System.IO; public partial class _Default : System.Web.UI.Page ...

  4. 漫话JavaScript与异步·第一话——异步:何处惹尘埃

    自JavaScript诞生之日起,频繁与异步打交道便是这门语言的使命,并为此衍生出了许多设计和理念.因此,深入理解异步的概念对于前端工程师来说极为重要. 什么是异步? 程序是分"块" ...

  5. css小知识 2

    效果为 为什么还出现出现不同的效果? 浏览器在解析第二个p的时候,因为第二个字母见没有空格,它会认为这是一个单词没有写完,所以不会换行 列表 1.无序列表ul 第二,内部必须有子代标签<li&g ...

  6. http状态码 以及请求响应头相关

    1xx消息[编辑] 这一类型的状态码,代表请求已被接受,需要继续处理.这类响应是临时响应,只包含状态行和某些可选的响应头信息,并以空行结束.由于HTTP/1.0协议中没有定义任何1xx状态码,所以除非 ...

  7. HTML知识梳理(笔记)

    HTML常见元素 meta 定义和用法<meta> 元素可提供有关页面的元信息(meta-information),比如针对搜索引擎和更新频度的描述和关键词. <meta> 标 ...

  8. 操作系统之IO管理

    IO系统结构 设备的分类 按数据组织分 块设备: 信息的存取总是以数据块为单位. 它属于有结构设备,如磁盘等. 磁盘设备的基本特征是传输速率较高,以及可寻址,即对它可随机地读/写任一块. 字符设备: ...

  9. TCP/IP基础总结性学习(6)

    HTTP 首部 一. HTTP 报文首部 1.HTTP 报文的结构: 2.HTTP 请求报文 图示: 举例子: 3.HTTP 响应报文: 下面的示例是访问 http://hackr.jp 时,请求报文 ...

随机推荐

  1. fpm工具安装

    概述 最近在对机房的编译环境做整理,过程曲折而痛苦,记录一下. 之前的一个老项目,在打包的时候用到了一个叫做fpm的工具. 编译环境涉及centos6和centos7,在新的编译环境的过程中,如何安装 ...

  2. 隐式转换导致的cpu负载近100%

    1.背景:从昨天晚上通过钉钉和邮箱一直接收到频繁报cpu负载超过90%,刚好BI同事晚上.凌晨在线上配合审计频繁DML数据库(备注:BI有一个同事有个库的DML权限,后面等审计完会收回)加上我线上线下 ...

  3. 【Golang】程序如何优雅的退出?

    1. 背景 项目开发过程中,随着需求的迭代,代码的发布会频繁进行,在发布过程中,如何让程序做到优雅的退出? 为什么需要优雅的退出? 你的 http 服务,监听端口没有关闭,客户的请求发过来了,但处理了 ...

  4. WAVE音频格式及及转换代码

    音频信号的读写.播放及录音 python已经支持WAV格式的书写,而实时的声音输入输出需要安装pyAudio(http://people.csail.mit.edu/hubert/pyaudio).最 ...

  5. 2 万字 + 30 张图 | 细聊 MySQL undo log、redo log、binlog 有什么用?

    作者:小林coding 计算机八股文网站:https://xiaolincoding.com/ 大家好,我是小林. 从这篇「执行一条 SQL 查询语句,期间发生了什么?」中,我们知道了一条查询语句经历 ...

  6. Vue3.0系列——「vue3.0性能是如何变快的?」

    前言 先学习vue2.x,很多2.x内容依然保留: 先学习TypeScript,vue3.0是用TS重写的,想知其然知其所以然必须学习TS. 为什么学习vue3.0? 性能比vue2.x快1.2-2倍 ...

  7. Consider defining a bean of type 'redis.clients.jedis.JedisPool' in your configuration.

    报错信息 原因是没有Jedispool没有注入 import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml ...

  8. 从UI Designer上面动态创建下拉列表

    在UI Desigher上创建一个新的列表 并创建2个值,code 和value 添加一个EventHandler 在EventHandler上面添加一个Operation 类型为script$dat ...

  9. 鹏城杯 WEB_WP

    简单的PHP GET /?code=[~%8C%86%8C%8B%9A%92][~%CF]([~%9A%91%9B][~%CF]([~%98%9A%8B%9E%93%93%97%9A%9E%9B%9A ...

  10. 【docker专栏5】详解docker镜像管理命令

    一.国内Docker镜像仓库 由于大家都知道的原因,从国外的docker 仓库中pull镜像的下载速度实际上是很慢的.国内的一些一线厂商以及docker官方都在国内免费提供了一些docker镜像仓库, ...