优雅的优化
分块
分块即为将整个序列分为多个块来处理区间信息,常的操作需支持修改和查询。
一个区间,它一定由一些完整的块和剩余的单点。对于完整的块,我们直接对整个块一起做修改和查询;对于剩余的单点,我们直接暴力修改。
通常块长被分为 \(\sqrt{n}\) ,时间复杂度为 \(O(n\sqrt{n})\)
代码模板:
int n, a[N]; //原数组
int p, pos[N]; //块长 和 每个点属于的块编号
int m, l[N], r[N]; //块的数量 和 每个块的左右端点
int b[N], k[N]; //查询用数组 和 记录块修改的数组
inline void work(){ //预处理
p = sqrt(n);
m = ceil(1.0 * n / p); //计算块数
for(int i = 1; i <= m; ++ i){
l[i] = r[i - 1] + 1;
r[i] = i * p; //计算块左右端点
for(int j = l[i]; j <= r[i]; ++ j){
pos[j] = i; //记录当前点块编号
b[i] = a[i]; //看题目要求
}
}
}
inline void add(int x, int y){ //修改区间[x, y]
for(int i = x; i <= min(y, r[pos[x]]); ++ i){ //左剩余区间
a[i] + ? //看题目要求
}
if(pos[x] != pos[y]){ //左右剩余不重
for(int i = max(l[pos[y]], x); i <= y; ++ i){ //右剩余区间
a[i] + ? //同上
}
}
for(int i = pos[x] + 1; i <= pos[y] - 1; ++ i){ //整块修改
k[i] + ?//同上
}
}
inline int ask(int x, int y){ //查询区间
int sum = 0;
for(int i = x; i <= min(y, r[pos[x]]); ++ i){ //左剩余区间
sum += a[i]? //看题目要求
}
if(pos[x] != pos[y]){ //左右剩余不重
for(int i = max(l[pos[y]], x); i <= y; ++ i){ //右剩余区间
sum += a[i]? //同上
}
}
for(int i = pos[x] + 1; i <= pos[y] - 1; ++ i){ //整块修改
sum += k[i]? //同上
}
return sum;
}
通常使用分块算法的数据范围为 \(n <= 1e5\) 或是 \(5e4\) 左右
st表
以 \(O(nlogn)\) 预处理,\(O(1)\) 查询的复杂度而闻名,用于线性求静态区间信息。
主体是运用倍增的思想,\(st[i][j]\) 维护区间 \((i, i + (1 << j) - 1)\) 的信息,由 \(st[i][j - 1]\) 和 \(st[i + (1 << (j - 1))][j - 1]\) 转移而来
求st表我们还需要一个 \(lg\) 数组,表示 \(log_2(i)\)
具体理解看代码吧
int lg[N];
int st[N][22];
inline int ask(int l, int r){
int k = lg[r - l + 1];
return max(st[l][k], st[r - (1 << k) + 1][k]);
// 查询区间最大值
}
int main(){
for(int i = 1; i <= n; ++ i){
st[i][0] = a[i];
}
for(int i = 2; i <= n; ++ i){
lg[i] = lg[i >> 1] + 1;
}
for(int i = 1; i <= lg[n]; ++ i){
for(int j = 1; j + (1 << i) - 1 <= n; ++ j){
st[j][i] = max(st[j][i - 1], st[j + (1 << (i - 1))][i - 1]);
// 此处为求区间最大值
}
}
}
st表求LCA
CSDN - st表求LCA
众所周知,dfs序……算了,我不会讲,看代码
\(O(1)\) 的查询,用过都说好
int dfn[N << 1], cnt, d[N];
//d 用来存每个节点第一次出现的位置
int lg[N << 1], st[N << 1][20];
// st 表维护区间深度最小值
inline void Dfs(int now, int fath){
dfn[++ cnt] = now;
st[cnt][0] = now;
d[now] = cnt;
for(int i = head[now]; i; i = e[i].nxt){
int v = e[i].to;
if(v != fath){
Dfs(v, now);
dfn[++ cnt] = now;
st[cnt][0] = now;
}
}
return ;
}
inline int Lca(int x, int y){
if(x > y) swap(x, y);
int k = lg[y - x + 1];
if(dep[st[x][k]] < dep[st[y - (1 << k) + 1][k]]){
return st[x][k];
}
else{
return st[y - (1 << k) + 1][k];
}
}
int main(){
Dfs(1, 0);
for(int i = 2; i <= cnt; ++ i){
lg[i] = lg[i >> 1] + 1;
}
//这样处理 st表 查询时可以直接返回节点编号
for(int i = 1; i <= lg[cnt]; ++ i){
for(int j = 1; j + (1 << i) - 1 <= cnt; ++ j){
if(dep[st[j][i - 1]] < dep[st[j + (1 << (i - 1))][i - 1]]){
st[j][i] = st[j][i - 1];
}
else{
st[j][i] = st[j + (1 << (i - 1))][i - 1];
}
}
}
}