分块 学习笔记

前言

忽然发现分块大法很好用,然而本蒟蒻不会…所以心血来潮学习了分块

例题

Code

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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
#include<algorithm>
#include<bitset>
#include<complex>
#include<deque>
#include<exception>
#include<fstream>
#include<functional>
#include<iomanip>
#include<ios>
#include<iosfwd>
#include<iostream>
#include<istream>
#include<iterator>
#include<limits>
#include<list>
#include<locale>
#include<map>
#include<memory>
#include<new>
#include<numeric>
#include<ostream>
#include<queue>
#include<set>
#include<sstream>
#include<stack>
#include<stdexcept>
#include<streambuf>
#include<string>
#include<typeinfo>
#include<utility>
#include<valarray>
#include<vector>
#include<cctype>
#include<cerrno>
#include<cfloat>
#include<ciso646>
#include<climits>
#include<clocale>
#include<cmath>
#include<csetjmp>
#include<csignal>
#include<cstdarg>
#include<cstddef>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
using namespace std;

#define re register
#define It std::set<int>::iterator
#define int long long

struct FastIO{
static const int S=1048576;
char buf[S],*L,*R;int stk[20],Top;
~FastIO(){clear();}
inline char nc(){return L==R&&(R=(L=buf)+fread(buf,1,S,stdin),L==R)?EOF:*L++;}
inline void clear(){fwrite(buf,1,Top,stdout);Top=0;}
inline void pc(char ch){Top==S&&(clear(),0);buf[Top++]=ch;}
inline void endl(){pc('\n');}
FastIO& operator >> (int& ret){
ret=0;int f=1;char ch=nc();
while(ch<'0'ch>'9'){if(ch=='-')f=-f;ch=nc();}
while(ch>='0'&&ch<='9'){ret=ret*10+ch-'0';ch=nc();}
ret*=f;return *this;
}
FastIO& operator >> (char* s){
re int Len=0;re char ch=nc();
while(ch!='\n'){
*(s+Len)=ch;
Len++;
ch=nc();
}
}
FastIO& operator << (int x){
if(x<0){pc('-');x=-x;}
do{stk[++stk[0]]=x%10;x/=10;}while(x);
while(stk[0]) pc('0'+stk[stk[0]--]);
return *this;
}
FastIO& operator << (char ch){pc(ch);return *this;}
FastIO& operator << (string str){
re int Len=str.size()-1;for(stk[0]=0;Len>=0;Len--) stk[++stk[0]]=str[Len];
while(stk[0]) pc(stk[stk[0]--]);
return *this;
}
}fin,fout;
int n,m,ans,a[100010],lef[100010],rig[100010],id[100010],sum[100010],add[100010],tot,sz;
#define File freopen("1.in","r",stdin);freopen("1.out","w",stdout);
inline void Print(){
for(int i=1;i<=tot;i++){
cout<<i<<":"<<lef[i]<<" "<<rig[i]<<" "<<add[i]<<" "<<sum[i]<<endl;
}
}
signed main(){
fin>>n>>m;
for(int i=1;i<=n;i++) fin>>a[i];
sz=sqrt(n);tot=sz;//每个块大小为sqrt(n)
if(sz*sz<=n) ++tot;//还有多余
for(int i=1;i<=n;i++){
id[i]=(i/sz)+(i%sz==0?0:1);//每个数所属的块的编号
sum[id[i]]+=a[i];//每个块内的和
if(id[i]==tot) lef[i]=sz*sz,rig[i]=n;//确定最后一个块的左右端点
lef[id[i]]=id[i]*sz-sz+1;rig[id[i]]=id[i]*sz;//确定这个块的左右端点
}
for(int op,l,r,v,i=1;i<=m;i++){
fin>>op>>l>>r;
if(op==1){
fin>>v;
for(int i=l;i<=min(r,rig[id[l]]);i++) a[i]+=v,sum[id[i]]+=v;//把左边的暴力加入
for(int i=max(l,lef[id[r]]);i<=r;i++) a[i]+=v,sum[id[i]]+=v;//把右边的暴力加入
for(int i=id[l]+1;i<=id[r]-1;i++) add[i]+=v;//把中间的加入
if(id[l]==id[r]) for(int i=l;i<=r;i++) a[i]-=v;//注意当l,r在同一个块内情况
}else{
ans=0;
for(int i=l;i<=min(r,rig[id[l]]);i++) ans+=a[i]+add[id[i]];//同上
for(int i=max(l,lef[id[r]]);i<=r;i++) ans+=a[i]+add[id[i]];//同上
for(int i=id[l]+1;i<=id[r]-1;i++) ans+=sum[i]+add[i]*(rig[i]-lef[i]+1);//同上
if(id[l]==id[r]) ans-=a[l]+a[r]+add[id[l]]+add[id[r]];//注意当l,r在同一个块内情况
fout<<ans<<"\n";
}
}
}