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 的头发。
现在把
H
a
n
d
I
n
D
e
v
i
l
\rm HandInDevil
HandInDevil 的头发抽象成一个数轴,一开始上面没有跳蚤。每一个时刻,有三种事件之一会发生:
- 有一个每次会跳
t
t
t 格的跳蚤跳上
H
a
n
d
I
n
D
e
v
i
l
\rm HandInDevil
HandInDevil 的头发的位置
x
x
x 。
- H
a
n
d
I
n
D
e
v
i
l
\rm HandInDevil
HandInDevil 开始挠头发,每一只跳蚤向右跳一次,跳的距离为各自的
t
t
t 。
- 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(QBnlogn) 。
然后对于
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 的头发 (分 块)的更多相关文章
- 列表 ul ol dl 和 块级标签和行及标签之间的转换
1. 无序列表 有序列表 自定义列表 1,无序列表 第一 你不必须有子标签 <li></li> 第二 ul天生自带内外边距 List-style的属性值 circle(空心圆 ...
- js瀑布流 原理实现揭秘 javascript 原生实现
web,js瀑布流揭秘 瀑布流再很久之前流行,可能如我一样入行晚的 ,可能就没有机会去使用.但是这个技术终究是个挺炫酷的东西,花了一个上午来研究,用原生js实现了一个,下面会附上源码,供大家解读. 说 ...
- ASP.NET 文件下载
using System; using System.Web; using System.IO; public partial class _Default : System.Web.UI.Page ...
- 漫话JavaScript与异步·第一话——异步:何处惹尘埃
自JavaScript诞生之日起,频繁与异步打交道便是这门语言的使命,并为此衍生出了许多设计和理念.因此,深入理解异步的概念对于前端工程师来说极为重要. 什么是异步? 程序是分"块" ...
- css小知识 2
效果为 为什么还出现出现不同的效果? 浏览器在解析第二个p的时候,因为第二个字母见没有空格,它会认为这是一个单词没有写完,所以不会换行 列表 1.无序列表ul 第二,内部必须有子代标签<li&g ...
- http状态码 以及请求响应头相关
1xx消息[编辑] 这一类型的状态码,代表请求已被接受,需要继续处理.这类响应是临时响应,只包含状态行和某些可选的响应头信息,并以空行结束.由于HTTP/1.0协议中没有定义任何1xx状态码,所以除非 ...
- HTML知识梳理(笔记)
HTML常见元素 meta 定义和用法<meta> 元素可提供有关页面的元信息(meta-information),比如针对搜索引擎和更新频度的描述和关键词. <meta> 标 ...
- 操作系统之IO管理
IO系统结构 设备的分类 按数据组织分 块设备: 信息的存取总是以数据块为单位. 它属于有结构设备,如磁盘等. 磁盘设备的基本特征是传输速率较高,以及可寻址,即对它可随机地读/写任一块. 字符设备: ...
- TCP/IP基础总结性学习(6)
HTTP 首部 一. HTTP 报文首部 1.HTTP 报文的结构: 2.HTTP 请求报文 图示: 举例子: 3.HTTP 响应报文: 下面的示例是访问 http://hackr.jp 时,请求报文 ...
随机推荐
- JavaScript之parseInt()方法
parseInt(string, radix):用于解析一个字符串并返回指定基数的十进制整数或者NaN string参数为被解析的值,如果该值不是一个字符串,则会隐式的使用toString()方法转化 ...
- 网络协议之:memcached binary protocol详解
目录 简介 memcached的协议包 memcached命令举例 总结 简介 前面讲到了memcached的文本协议,虽然文本协议看起来非常简单,但是对于客户端来说一般还是会选择效率更高的二进制协议 ...
- ssh隧道连接的方式连接数据库
最好用xshell做隧道连接,其他工具没接触过过 1.先新建一个会话 2.点进刚刚建好的连接,右击属性 3.点进隧道,添加,输入映射到本地的配置 4.完成之后用数据库连接工具连接即可 参考连接: ht ...
- Tomcat部署界面使用burp爆破
打开界面显示私密连接,正常抓包. 抓包查看Authorization的数据 Basic 后面的数据是经过base64加密后的.格式为admin:123456 勾选对应参数,payload设置为Cust ...
- WPF开发随笔收录-心电图曲线绘制
一.前言 项目中之前涉及到胎儿心率图曲线的绘制,最近项目中还需要添加心电曲线和血样曲线的绘制功能.今天就来分享一下心电曲线的绘制方式: 二.正文 1.胎儿心率曲线的绘制是通过DrawingVisual ...
- Flex & Bison 开始
Flex 与 Bison 是为编译器和解释器的编程人员特别设计的工具: Flex 用于词法分析(lexical analysis,或称 scanning),把输入分割成一个个有意义的词块,称为记号(t ...
- 微服务追踪SQL(支持Isto管控下的gorm查询追踪)
效果图 SQL的追踪正确插入到微服务的调用链之间 详细记录了SQL的执行内容和消耗时间 搜索SQL的类型 多线程(goroutine)下的追踪效果 在 Kubernetes 中部署微服务后,通过 Is ...
- Nginx工作模式
Master-Worker模式 1.Nginx 在启动后,会有一个 master 进程和多个相互独立的 worker 进程.2.接收来自外界的信号,向各worker进程发送信号,每个进程都有可能来处理 ...
- Re:用webpack从零开始的vue-cli搭建'生活'
有了vue-cli的帮助,我们创建vue的项目非常的方便,使用vue create然后选择些需要的配置项就能自动帮我们创建配置好的webpack项目脚手架了,实在是'居家旅行'必备良药.这次借着学习w ...
- Solution -「简单 DP」zxy 讲课记实
魔法题位面级乱杀. 「JOISC 2020 Day4」治疗计划 因为是不太聪明的 Joker,我就从头开始理思路了.中途也会说一些和 DP 算法本身有关的杂谈,给自己的冗长题解找借口. 首先,治疗方案 ...