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(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 = 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;
}