分块算法

分块事实上是一种优化的暴力,其空间复杂度是O(mn^1/2)

直接开始说分块是什么东西

当然这里讲解的是noip阶段的分块,我并不知道怎么用分块维护单调队列

那么这种我说的分块只能作为线段树的替代,如果你不会线段树和树状数组的话

例如:给你n个数,m个操作 n,m<=10^5

操作1:询问[l,r]的区间和

操作2:修改[l,r]的值为原来的值+v

我在之前的线段树给出了单点修改,区间查询,显然的对于这里,加上循环肯定是GG的

当然打标记nlogn随便过

树状数组随便过

假设我们都不会

从最简单的枚举

O(mn)  我们有一种看似没用的优化-----循环展开

int ans = 0;
for(int i = 1;i <= n;i++){
 ans += i;
}
for(int a = ;a <= n;a += 2){
 ans += a;
 ans + =a+1;
}

这个只能快常数级别,但这种思想我们是需要的

我们再次考察这n个数

我们把这n个数分成a块 每块长度为s 显然a = n/s

考察询问 暴力的话是从1一个个往右走 显然我们是不需要一个个遍历的,中间多了许多没有必要地枚举

我们枚举完走完第一块的时候,如果下一块中没有r我们可以直接记录区间值,然后跳过

如果遇到了r 暴力枚举就好了

好-------这就是分块的基本思想

但你可能有一个问题

这个s 我们要怎么衡量呢

我们考察它的复杂度

显然第一段和最后一段我们都要暴力扫完

中间的就是其中的块数去掉常数就是O(2s+N/s) 然后用初等的知识

可以理解成耐克函数也可以理解成均值不等式

显然可知当且仅当s = n^1/2时时间最小

这是一个O(n^1/2*m)的比较优秀的暴力

那么修改呢,是不是看起来就很简单了

但有一个问题,我们怎么维护块内的和

你自然会想到我们可以在经过的块上vs

我们由线段树打标记的思想可以知道,我们可以模仿一个colour函数

剩下的两个部分暴力就好了

总结一下

我们在暴力的过程中需要维护3个值

1. z 当前值2.ink 每个快总体上被加上多少3.sum

 在这个题目上是没有线段树好的,而且可以预见到这个代码量不会比线段树少多少

但是,那我们为什么在noip阶段还要讲这个呢

因为你线段树代码有可能被不过 现写很难,这个简单一些,树状数组好背但并不好理解,更不好写代码

瞎搞了一波基本思想,但是----------------

由上面写出的题解是不对的→.→

我们要特判r,l在一个块的情况嘿嘿嘿

代码如下

#include<iostream>
#include<cstdio>
#include<cmath>

const int N = 10010

using namespace std;

int s = (int)sqrt(n);  //s 赋初值
int belong[N];
int query(int l,int r);

int main(){
 for(int a = 1;a <= n;a++){
  belong[a] = (a - 1) / (s + 1)  //预处理
 }
 for(int a = 1;a <= n;a++){
  right[belong[a]] == a;
 }
 for(int a = 1;a <= n;a++){
        sum[belong[a]] += z[a];
 }
 for(int a = n;a >= 1;a--){
  left[belong[a]] = a;
 }
}

int query(int l,int r){
 int ans = 0;
 if(belong[l]  == belong[r]){
  for(int i = 1;i <= r;i++){
   ans += z[a] + col[belong[a]]
  }
 }  // 特判的内容判断lr是否在同一块
 else {
  for(int a = 1;a <= right[belong[l]];a++){
   ans += z[a] + col[belong[a]];
  }
  for(int a = belong[l] + 1;a < belong[r];a++){
   ans += sum[a];
  }
  for(int a = left[belong[r]];a <= r;a++){
   ans += z[a] + col[belong[a]]
  }
 }
 return ans;
}

void modify(int l,int r,inr v){
 if(belong[l] == belong[r]){
  for(int a = l;a <= r;a++){
   z[a] += v;
   sum[belong[a]] += v;
  }
 }
 else{
  for(int a = q;a <= right[belong[l]];a++){
   z[a] += v;
   sum[belong[a]] += v;
  }
  for(int i = belong[l] + 1;i < belong[r];a++){
   col[a] += v;
  }
  for(int a = left[belong[r]];a <= r;a++){
   z[a] += v;
   sum[belong[a]] += v;
  }
 }
}

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值