题面

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. Docker-Compose实现Mysql主从

    1. 简介 通过使用docker-compose 搭建一个主从数据库,本示例为了解耦 将两个server拆分到了两个compose文件中,当然也可以放到一个compose文件中 演示mysql版本:5 ...

  2. vue项目经常遇到的Error: Loading chunk * failed

    vue项目随着代码量.业务组件.路由页面等的丰富,出于性能要求考虑不得不使用代码分割技术实现路由和组件的懒加载,这看似没什么问题 当每次通过npm run build构建生产包并部署到服务器后,操作页 ...

  3. SQL语句修改MySQL用户密码

    SQL语句修改MySQL用户密码 前言 上数据库安全实验课,用命令行和DataGrip试图修改用户密码,一直语法报错.最后用Navicat才修改成功,预览Navicat的SQL语句,发现语句和网上都不 ...

  4. vue基本原理

    当一个Vue实例创建时,Vue会遍历data中的属性,用Object.defineProperty(vue3.0使用proxy)将它们转为getter/setter,并且在内部追踪相关依赖,在属性被访 ...

  5. JavaScript做简单的购物车效果(增、删、改、查、克隆)

    比如有时候遇到下面这种情况,点击加入购物车,然后在上方的购物车中动态的添加商品以及商品的信息,我们就可以通过JavaScript实现简单的这些操作. 首先我们需要在html文档中,通过css对页面的布 ...

  6. 原生实现.NET5.0+ 自定义日志

    一.定义一个静态类 声明一个 ReaderWriterLockSlim 对象 用于并发控制 1 /// <summary> 2 /// IO锁 3 /// </summary> ...

  7. 用python随随便便做一个二维码叭~~~

    Python是目前最好的编程语言之一.由于其可读性和对初学者的友好性,已被广泛使用. 那么要想学会并掌握Python,可以实战的练习项目是必不可少的. 接下来,我将给大家介绍非常实用的Python项目 ...

  8. Python控制自己的手机摄像头拍照,并把照片自动发送到邮箱

    写在前面的一些P话: 今天这个案例,就是控制自己的摄像头拍照,并且把拍下来的照片,通过邮件发到自己的邮箱里.想完成今天的这个案例,只要记住一个重点:你需要一个摄像头 思路 通过opencv调用摄像头拍 ...

  9. linux配置svn

    1.安装 yum install subversion 2.测试安装是否成功: svnserve --version 3.创建目录并配置 建立版本库目录 mkdir -pv /data/svn/svn ...

  10. Unsupported major.minor version 52.0 (unable to load class org.apache.kafka.clients.producer.Produce异常解决方法

    在控制台输入java -version,查看自己的版本是多少,我的查出来是1.8的.随后将服务器上的改为1.8的就可以了.