突刺贯穿的第二分块
Posted Modirant
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了突刺贯穿的第二分块相关的知识,希望对你有一定的参考价值。
[CF896E] Welcome home, Chtholly
给定一个长为 \\(n\\) 的序列 \\(a_1\\sim a_n\\),\\(m\\) 次操作,分两种:
1 l r x
,将 \\(a_l\\sim a_r\\) 中 \\(\\gt x\\) 的数减去 \\(x\\)。
2 l r x
,询问此时 \\(a_l\\sim a_r\\) 有多少数等于 \\(x\\)。\\(1\\le n,m,a_i,x\\le 10^5\\)。
lxl 的分块科技,很奇妙。
考虑根据这个特殊的值域搞点什么东西。
分块以后可以直接在每个块内开 \\(10^5\\) 个桶存这个块里的数。这样询问操作就可以解决了。
考虑修改操作,散块重构,对于整块,两种暴力:
-
给 \\(\\le x\\) 的数加上 \\(x\\),然后给整个块打上一个整体减 \\(x\\) 的 tag。
-
枚举 \\(\\gt x\\) 的桶 \\(i\\),将桶 \\(i\\) 和 \\(i-x\\) 合并。
单次操作 \\(\\mathcal O(V)\\),这是坏的。考虑平衡思想,将两者结合。记录整块最大值 \\(\\rm maxv\\),分类:
-
\\(\\mathrmmaxv\\le 2x\\),采用暴力 2.
-
\\(\\mathrmmaxv\\gt 2x\\),采用暴力 1.
相当于用 \\(\\mathcal O(Dx)\\) 的代价使得 \\(\\rm maxv\\) 减小了 \\(x\\)。总复杂度 \\(\\mathcal O(Dn\\sqrt n)\\),这是好的。
考虑用一个 \\(\\mathcal O(1)\\) 复杂度的数据结构维护桶的合并。使用并查集,因为每条边至多被遍历一遍,单次复杂度均摊 \\(\\mathcal O(1)\\)。
强化版卡空间,可以离线,对于每个块单独计算对所有询问的贡献。
#include <bits/stdc++.h>
const int maxn = 1e5 + 5;
const int S = 400;
int read()
int s = 0;
char c = getchar();
for(;c < \'0\'||c > \'9\';c = getchar());
for(;c >= \'0\'&&c <= \'9\';c = getchar())
s = (s << 1) + (s << 3) + (c ^ \'0\');
return s;
void print(int x)
if(x > 9)
print(x / 10);
putchar(x % 10 + \'0\');
return ;
int pre[maxn],val[maxn];
int find(int x)
return x == pre[x] ? x : pre[x] = find(pre[x]);
struct node
int rt,sz;
node()
rt = sz = 0;
d[S + 5][maxn];
int max(const int& x,const int& y)
return x > y ? x : y;
int min(const int& x,const int& y)
return x < y ? x : y;
int n,m,a[maxn],t,maxv[S],L[S],R[S],pos[maxn],tag[S];
void pushdown(int p)
for(int i = L[p];i <= R[p];++ i)
a[i] = val[find(i)],d[p][a[i]].rt = d[p][a[i]].sz = 0,a[i] -= tag[p];
tag[p] = 0;
for(int i = L[p];i <= R[p];++ i)
pre[i] = 0;
return ;
void Maintain(int p)
maxv[p] = 0;
for(int i = L[p];i <= R[p];++ i)
maxv[p] = max(maxv[p] , a[i]);
++ d[p][a[i]].sz;
if(!d[p][a[i]].rt)
d[p][a[i]].rt = pre[i] = i,val[i] = a[i];
else
pre[i] = d[p][a[i]].rt;
return ;
void Merge(int p,int x,int y)
if(d[p][y].rt)
pre[d[p][x].rt] = d[p][y].rt;
else
d[p][y].rt = d[p][x].rt,val[d[p][x].rt] = y;
d[p][y].sz += d[p][x].sz;
d[p][x].sz = d[p][x].rt = 0;
return ;
void gettag(int p,int x)
if(maxv[p] - tag[p] < (x << 1))
for(int i = maxv[p];i > x + tag[p];-- i)
if(d[p][i].rt)
Merge(p , i , i - x);
maxv[p] = min(maxv[p] , x + tag[p]);
else
for(int i = tag[p] + 1;i <= x + tag[p];++ i)
if(d[p][i].rt)
Merge(p , i , i + x);
tag[p] += x;
return ;
void modify(int l,int r,int x)
int p = pos[l],q = pos[r];
if(p == q)
pushdown(p);
for(int i = l;i <= r;++ i)
if(a[i] > x)
a[i] -= x;
Maintain(p);
else
pushdown(p);
pushdown(q);
for(int i = l;i <= R[p];++ i)
if(a[i] > x)
a[i] -= x;
for(int i = L[q];i <= r;++ i)
if(a[i] > x)
a[i] -= x;
Maintain(p);
Maintain(q);
for(int i = p + 1;i < q;++ i)
gettag(i , x);
return ;
int query(int l,int r,int x)
int ans = 0,p = pos[l],q = pos[r];
if(p == q)
for(int i = l;i <= r;++ i)
if(val[find(i)] - tag[p] == x)
++ ans;
else
for(int i = l;i <= R[p];++ i)
if(val[find(i)] - tag[p] == x)
++ ans;
for(int i = L[q];i <= r;++ i)
if(val[find(i)] - tag[q] == x)
++ ans;
for(int i = p + 1;i < q;++ i)
if(x + tag[i] <= 100000)
ans += d[i][x + tag[i]].sz;
return ans;
int main()
n = read();
m = read();
for(int i = 1;i <= n;++ i)
a[i] = read(),pos[i] = (i - 1) / S + 1;
for(int i = 1;i <= n;++ i)
if(!L[pos[i]])
L[pos[i]] = i;
R[pos[i]] = i;
for(int i = 1;i <= pos[n];++ i)
Maintain(i);
while(m --)
int op = read(),l = read(),r = read(),x = read();
if(op == 1)
modify(l , r , x);
else
print(query(l , r , x)),putchar(\'\\n\');
return 0;
以上是关于突刺贯穿的第二分块的主要内容,如果未能解决你的问题,请参考以下文章
Science首发奥密克戎突刺蛋白分子水平分析,揭秘2大传染性增强原因,柳叶刀:全球大流行有望3月结束...
阿里P8日记:十亿级流量下,我与Redis时延小突刺的战斗史