P4117 【[Ynoi2018]五彩斑斓的世界】题解
P4117 【[Ynoi2018]五彩斑斓的世界】
"突刺贯穿的第二分块"
调了一天的第二分块最后发现是 memset 的问题。。。(机房巨佬nb)
大致思路相信第一篇题解已经讲的很清楚了,这里珂以再作阐述:
考虑分块:对于每一个大小为 \(\sqrt{n}\) 的块,
先考虑修改:
设整个块的最大值为 maxn ,修改值为 x 珂以分两种情况处理:
-
\(x*2<=maxn-tag\), 对于这个块里的值,如题意一般,将所有\(\geq x\)的数都\(-x\),修改后的 \(maxn\) 依旧不变
-
\(x*2>maxn-tag\) 对于整个块的操作其实也就等价于将所有\(\leq x\)的\(+x\),再将整个区间打上一个\(-x\)的 \(tag\)
再用一个并查集维护值域,
记录下这个值下的\(size\),与这个值第一次出现位置\(st\),还有第一次出现位置所对应的映射 \(rev\)
操作1也就相当于将\(a\in(tag+x,maxn]\)中的每个数都与\(a-x\)合并
操作2也就相当于先将\(a \in [tag+1,x+tag]\) 与\(a+x\) 合并,再打上一个 \(-x\) 的 \(tag\)
对于不属于整块的操作直接暴力重新构造就好了
查询的时候整块直接查 \(x+tag\) 的 \(size\)
不属于的直接暴力求解就好了
然后出题人的题解中,有一个逐块处理的trick
大致就是统计每一个块对于每一个询问的贡献,这样就可以做到\(O(N+M)\)
这个优化的很多前提珂以看这篇题解
还有记住每一次统计的时候记得清空并查集中的信息,但是重构的时候不用清空,(我的惨烈调试记录)
丑陋的代码~~~
#include<bits/stdc++.h>
using namespace std;
inline int read(){
int x=0,f=1; char ch=getchar();
while(!isdigit(ch)){if(ch=='-'){f=-1;}ch=getchar();}
while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch-'0');ch=getchar();}
return x*f;
}
inline void write( int x){
if(x==0){putchar(48);putchar('\n');return;}int len=0,dg[20];
while(x>0){dg[++len]=x%10;x/=10;}
for( int i=len;i>=1;i--) putchar(dg[i]+48);
putchar('\n');
}
const int MAXN=1e6+5,N=1e6+5,SQRTN=1e4+5,M=1e6+5;;
int n,m,L[MAXN],R[MAXN],a[N],ans[M];
int st[MAXN],fa[MAXN],size[MAXN],rev[MAXN],maxn,tag_dec;
int ql[M],qr[M],qx[M],op[M],len;
inline int get(int x){return fa[x]==x ? x : fa[x]=get(fa[x]);}
inline void merge(int x,int y){
if(st[y]){fa[st[x]]=st[y];}
else{st[y]=st[x];rev[st[y]]=y;}
size[y]+=size[x];st[x]=size[x]=0;
}
inline void pre_build(){
len=sqrt(n);
for( int i=1;i<=len;++i){L[i]=(i-1)*len+1;R[i]=i*len;}
if(R[len]<n){len++;L[len]=R[len-1]+1;R[len]=n;}
}
inline void build_each(int num){
maxn=0,tag_dec=0;
for( int i=L[num];i<=R[num];++i){
maxn=max(maxn,a[i]);
if(st[a[i]]){fa[i]=st[a[i]];}
else{st[a[i]]=fa[i]=i;rev[i]=a[i];}
size[a[i]]++;
}
}
inline void modify(int x){
if(maxn-tag_dec>=x*2){
for( int i=tag_dec+1;i<=tag_dec+x;++i){if(st[i]){merge(i,x+i);}}
tag_dec+=x;
}
else{
for( int i=x+tag_dec+1;i<=maxn;++i){if(st[i]){merge(i,i-x);}}
maxn=min(maxn,tag_dec+x);
}
}
inline void rebuild(int num,int queryl,int queryr,int x){
for( int i=L[num];i<=R[num];++i){
a[i]=rev[get(i)];
st[a[i]]=size[a[i]]=0;
a[i]-=tag_dec;
}
for( int i=L[num];i<=R[num];++i){rev[i]=0;}
int lim=min(queryr,R[num]);
int liml=max(L[num],queryl);
for( int i=liml;i<=lim;++i){a[i]=a[i]-(a[i]>x)*x;}
build_each(num);
}
inline void query(int num,int querynum){
int x=qx[querynum],queryr=qr[querynum],queryl=ql[querynum];
if(x+tag_dec>5e5){return;}
if(queryl<=L[num]&&R[num]<=queryr){ans[querynum]+=size[x+tag_dec];}
else{
int lim=min(queryr,R[num]);
int liml=max(queryl,L[num]);
for(int i=liml;i<=lim;++i){if(rev[get(i)]==x+tag_dec){ans[querynum]++;}}
}
}
inline void check(){puts("??????woc?????");}
int main(){
n=read(),m=read();
for( int i=1;i<=n;++i){a[i]=read();}
pre_build();
for( int i=1;i<=m;++i){op[i]=read(),ql[i]=read(),qr[i]=read(),qx[i]=read();}
for( int i=1;i<=len;++i){
memset(st,0,sizeof(st));
memset(size,0,sizeof(size));
build_each(i);
for( int j=1;j<=m;++j){
if(qr[j]<L[i]||ql[j]>R[i]){continue;}
if(op[j]==1){
if(L[i]>=ql[j]&&R[i]<=qr[j]){modify(qx[j]);}
else{rebuild(i,ql[j],qr[j],qx[j]);}
}
else{query(i,j);}
}
}
for(int i=1;i<=m;++i){if(op[i]==2){write(ans[i]);}}
return 0;
}