【分块+并查集】Codeforces 896E Welcome home, Chtholly

题面在这里

考虑分块,两端的散块都可以暴力搞了

对于中间的整块,维护最大值\(mx\),减标记\(tag\),用并查集把相同值连在一起

  • 修改:分两类做
    • \(mx\gt 2x\):先把值在\([1,x]\)的数加\(x\),再把整个块减\(x\)
    • \(2x\ge mx\):把值在\([x+1,mx]\)的数减\(x\)
  • 询问:由并查集连通块大小直接得到

复杂度证明:分块的复杂度是\(O(n\sqrt n)\),现在只要证明对整块单次修改的复杂度

考虑块内的极差,修改操作其实在\(O(x)\)的时间内把极差减小了\(x\)

所以整块单次修改均摊是\(O(1)\)

示例程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
inline char nc(){
static char buf[100000],*l=buf,*r=buf;
return l==r&&(r=(l=buf)+fread(buf,1,100000,stdin),l==r)?EOF:*l++;
}
inline int red(){
int res=0,f=1;char ch=nc();
while (ch<'0'||'9'<ch) {if (ch=='-') f=-f;ch=nc();}
while ('0'<=ch&&ch<='9') res=res*10+ch-48,ch=nc();
return res*f;
}

const int maxn=100005,maxb=320;
int n,m,q,a[maxn],fa[maxn];
inline int getfa(int x) {return fa[x]==x?x:fa[x]=getfa(fa[x]);}
int rt[maxb][maxn],cnt[maxb][maxn],mx[maxb],h[maxn],bg[maxb],ed[maxb],tag[maxb];
void blocker(){
int S=sqrt(n);
for (int i=1;i<=n;i++){
if (S*m<i) bg[++m]=i;
h[i]=m;
if (i%S==0||i==n) ed[m]=i;
}
}
void rebuild(int b){
mx[b]=0;
for (int i=bg[b];i<=ed[b];i++){
if (!rt[b][a[i]]){
rt[b][a[i]]=i;
fa[i]=i;
cnt[b][a[i]]=1;
}else{
fa[i]=rt[b][a[i]];
cnt[b][a[i]]++;
}
mx[b]=max(mx[b],a[i]);
}
}
int tmp[maxn];
void pushdown(int b){
for (int i=bg[b];i<=ed[b];i++)
if (getfa(i)==i) rt[b][a[i]]=0,cnt[b][a[i]]=0;
for (int i=bg[b];i<=ed[b];i++) tmp[i]=a[i];
for (int i=bg[b];i<=ed[b];i++) a[i]=tmp[getfa(i)]-tag[b];
tag[b]=0;
}
void ist_force(int l,int r,int x){
for (int i=h[l];i<=h[r];i++) pushdown(i);
for (int i=l;i<=r;i++)
if (a[i]>x) a[i]-=x;
for (int i=h[l];i<=h[r];i++) rebuild(i);
}
int qry_force(int l,int r,int x){
int res=0;
for (int i=h[l];i<=h[r];i++) pushdown(i);
for (int i=l;i<=r;i++) res+=(a[i]==x);
for (int i=h[l];i<=h[r];i++) rebuild(i);
return res;
}
void merge(int &rt1,int &rt2,int &c1,int &c2,int x){
if (rt1)
if (rt2) fa[rt1]=rt2,c2+=c1;
else rt2=rt1,a[rt2]=x,c2=c1;
rt1=c1=0;
}
void ist(int b,int x){
if (mx[b]-tag[b]>2*x){
for (int i=1+tag[b];i<=x+tag[b];i++) merge(rt[b][i],rt[b][i+x],cnt[b][i],cnt[b][i+x],i+x);
tag[b]+=x;
}else{
for (int i=x+1+tag[b];i<=mx[b];i++) merge(rt[b][i],rt[b][i-x],cnt[b][i],cnt[b][i-x],i-x);
while (!rt[b][mx[b]]) mx[b]--;
}
}
int qry(int b,int x){
return x+tag[b]<=100000?cnt[b][x+tag[b]]:0;
}
int main(){
n=red(),q=red();
for (int i=1;i<=n;i++) a[i]=red();
blocker();
for (int i=1;i<=m;i++) rebuild(i);
while (q--){
int c=red(),l=red(),r=red(),x=red();
if (c==1){
if (h[l]==h[r]) ist_force(l,r,x);else{
ist_force(l,ed[h[l]],x);
ist_force(bg[h[r]],r,x);
for (int i=h[l]+1;i<h[r];i++) ist(i,x);
}
}else{
int ans=0;
if (h[l]==h[r]) ans=qry_force(l,r,x);else{
ans+=qry_force(l,ed[h[l]],x);
ans+=qry_force(bg[h[r]],r,x);
for (int i=h[l]+1;i<h[r];i++) ans+=qry(i,x);
}
printf("%d\n",ans);
}
}
return 0;
}