前几天写莫队有点上瘾,想起好久没有写过分块了,就来写一道分块题
题意很简单
给定一个序列,要求维护以下操作
把区间([l,r])中所有大于(x)的数减去(x)
查询区间([l,r])中等于(x)的数的个数
很显然的,这些操作都主要与权值有关
所以按权值维护
但是任意区间不方便维护,考虑分块
同一个块中如果只有整块的修改
显然如果两个位置权值一样
不管怎么改还是一样的
所以可以用并查集
只修改一个点,合并也是(O(1))的
但是如果直接修改所有大于(x)的显然会超时
所以如果大于(x)的比较少就直接修改大于(x)的(复杂度(O(max-x)),(max)是块内最大值)
否则修改小于等于(x)的部分(加上(x)),然后打一个整块减的标记(复杂度(O(x)))
具体来说如果(x*2>max)就改大的部分否则改小的部分
因为每次修改都会使(max)减小(x)且每次复杂度都是(O(x))
所以均摊复杂度(O(n*sqrt n))
然后写出来,调试了半天,终于过了
然后打开五彩斑斓的世界
直接把Welcome home, Chtholly
的代码交上去
MLE
,(30pts)
发现空间限制变成了原来的(frac{1}{4})
卡空间后在BZOJ
交了一发,过了
兴高采烈地又去洛谷
交了一发,TLE
(90pts)
发现时间限制变成了原来的(frac{1}{3})
于是又卡了半个小时的常
最后改得无比丑陋终于卡过了
(100pts)代码惨不忍睹
还是贴(90pts)的吧
#include<bits/stdc++.h>
using namespace std;
#define gc c=getchar()
#define r(x) read(x)
template<typename T>
inline void read(T&x){
x=0;T k=1;char gc;
while(!isdigit(c)){if(c=='-')k=-1;gc;}
while(isdigit(c)){x=x*10+c-'0';gc;}x*=k;
}
const int N=1e5+7;
const int len=320;
int fa[N];
inline int find(int x){
return x==fa[x]?x:fa[x]=find(fa[x]);
}
int mx[len],L[len],R[len];
int rt[len][N];
unsigned short cnt[N];
int a[N];
inline void build(int x){
mx[x]=0;
for(int i=L[x];i<=R[x];++i){
if(!rt[x][a[i]]){
rt[x][a[i]]=i;
fa[i]=i;
cnt[rt[x][a[i]]]=1;
mx[x]=max(mx[x],a[i]);
}
else {
fa[i]=rt[x][a[i]];
cnt[rt[x][a[i]]]++;
}
}
}
int tag[len];
inline void pushdown(int x){
for(int i=L[x];i<=R[x];++i){
a[i]=a[find(i)];
cnt[rt[x][a[i]]]=0;
rt[x][a[i]]=0;
}
for(int i=L[x];i<=R[x];++i)a[i]-=tag[x];
for(int i=L[x];i<=R[x];++i)fa[i]=0;
tag[x]=0;
}
unsigned short be[N];
inline void modify1(int l,int r,int v){
for(int i=be[l];i<=be[r];++i)pushdown(i);
for(int i=l;i<=r;++i)if(a[i]>v)a[i]-=v;
for(int i=be[l];i<=be[r];++i)build(i);
}
inline void insert(int x,int p,int q){
if(!rt[x][p])return;
if(!rt[x][q]){
rt[x][q]=rt[x][p];
cnt[rt[x][q]]=cnt[rt[x][p]];
a[rt[x][p]]=q;
}
else {
fa[rt[x][p]]=rt[x][q];
cnt[rt[x][q]]+=cnt[rt[x][p]];
}
rt[x][p]=0;
cnt[rt[x][p]]=0;
}
inline void modify2(int x,int v){
if(v>=mx[x]-tag[x])return;
if(v*2>mx[x]-tag[x]){
for(int i=v+tag[x]+1;i<=mx[x];++i)insert(x,i,i-v);
mx[x]=max(mx[x]-v,v+tag[x]);
}
else {
for(int i=tag[x]+1;i<=tag[x]+v;++i)insert(x,i,i+v);
tag[x]+=v;
}
}
inline void modify(int l,int r,int v){
if(be[l]+1>=be[r])modify1(l,r,v);
else {
modify1(l,R[be[l]],v);
modify1(L[be[r]],r,v);
for(int i=be[l]+1;i<be[r];++i)modify2(i,v);
}
}
inline int query1(int l,int r,int v){
int ret=0;
for(int i=l;i<=r;++i)if(a[find(i)]-tag[be[i]]==v)ret++;
return ret;
}
inline int query2(int x,int v){
if(v+tag[x]>1e5)return 0;
return cnt[rt[x][v+tag[x]]];
}
inline int query(int l,int r,int v){
if(be[l]+1>=be[r])return query1(l,r,v);
else {
int ret=query1(l,R[be[l]],v)+query1(L[be[r]],r,v);
for(int i=be[l]+1;i<be[r];++i)ret+=query2(i,v);
return ret;
}
}
int main(){
int n,m;r(n),r(m);
int block_size=sqrt(n);
for(int i=1;i<=n;++i){
r(a[i]);
be[i]=(i-1)/block_size+1;
if(!L[be[i]])L[be[i]]=i;
R[be[i]]=i;
}
for(int i=1;i<=be[n];++i)build(i);
for(int opt,l,r,x;m;--m){
r(opt),r(l),r(r),r(x);
if(opt==1)modify(l,r,x);
else printf("%d
",query(l,r,x));
}
}