首页 > 编程语言 >算法思想

算法思想

时间:2023-04-05 16:11:26浏览次数:43  
标签:le 思想 sum 差分 算法 数组 mathcal 指针

\(\mathcal{Part}\) 1. 前提提要

注意:本文为提高组难度的算法思想,主要为前缀和,差分等优化

因为是思想,讲的会比较玄乎,能理解就好

\(\mathcal{Part}\) 2. 双指针

双指针通常解决区间问题

步骤是,确定一个右节点或左节点作为一个参考点,通常取右节点,记为 \(j\)

我们考虑一个刚好符合题目条件的最小节点,记为 \(l\)

双指针有个前提:当右指针往右移动时,左指针不可能往前走

解决步骤:

右指针往右跳,让左指针也往右跳,跳到符合条件为止,这就是题目要求的以 \(r\) 为右端点最长的区间

因为左指针和右指针都只会跳 \(n\) 次,总时间复杂度为 \(\mathcal{O}(n)\)

\(\mathcal{Practice}\) 1.

P1638逛画展

\(\mathcal{Practice}\) 2.

P1102 A-B数对

\(\mathcal{Part}\) 3. 前缀和

一维前缀和

我们有一个序列 \(A\),我们要进行一下操作

  • \(\mathcal{O}(n)\) 地预处理
  • 给定一个 \(l\) 和 \(r\),求 \(\sum_{l\le i\le r} A_i\),时间复杂度为 \(\mathcal{O}(1)\)

我们记一个数组 \(B\) 满足

\[B_i=\sum_{1\le j\le i} A_j \]

预处理 \(B\) 数组,时间复杂度为 \(\mathcal{O}(n)\)

我们要查询 \([l,r]\) 的值,也就是 \(\sum_{l\le i\le r} A_i\),经过简单数学推导,如下

\[\begin{aligned} \sum_{l\le i\le r}A_i&=\sum_{1\le j\le r}A_j - \sum_{1\le k <l}A_k\\ &=B_r-B_{l-1} \end{aligned} \]

通俗点来说,就是 \([l,r]\) 的值等于 \(B_r-B_{l-1}\)

前缀和在众多领域起着重大作用,以代码量小和快著称

弊端是:不能动态修改

二维前缀和

给定一个二维序列,记为 \(A\),现要支持

  • 给定两点 \((l_1,r_1)\) 和 \((l_2,r_2)\),求以两点为矩形端点的矩形,里面所有数之和

记一个二维数组 \(B_{i,j}\) 为 \((1,1)\) 到 \((i,j)\) 的数之和

初始化 \(B_{i,j}=B_{i-1,j}+B_{i,j-1}-B_{i-1,j-1}+A_{ij}\)

证明平凡,自己画图

则两点 \((l_1,r_1)\) 和 \((l_2,r_2)\),求以两点为矩形端点的矩形,里面所有数之和就为:

\[B_{l2,r2}-B_{l1-1,r2}-B_{l2,r1-1}+B_{l1-1,r2-1} \]

推导过程平凡,请自己画图证明

多维前缀和

类比容斥原理

\(\mathcal{Practice}\)

见题单

\(\mathcal{Part}\) 4. 差分

一维差分

现有一个序列 \(A\),先要支持

  • 动态修改 \([l,r]\) 的值
  • \(O(n)\) 打印 \(A\) 序列的每个数

记一个数组 \(B\) 满足

\[B_i=A_i-A_{i-1} \]

我们看看 \(A_i\) 等与什么

\[\sum_{1\le j \le i}B_j=\sum_{1\le j\le i}A_j-A_{j-1}=A_i \]

也就是说,差分的前缀和等于原数组,简单的想,前缀和的差分等于差分

这三者就有机的连在一起了

接下来看怎么修改,记修改的数为 \(k\)

  • 当 \(i=l\) 时,\(B_i'=(A_i+k)-A_{i-1}=B_i+k\)
  • 当 \(i = r+1\) 时, \(B_i'=(A_{r+1}-(A_{r}+k))=B_i-k\)
  • 当 \(i\in (l,r]\) 时,\(B_i'=(A_i+k)-(A_{i-1}+k)=B_i\)

通俗点说,就是修改 \([l,r]\) 的值时,只需要修改 \(B_l\) 和 \(B_{r+1}\) 的值即可

时间复杂度为:\(\mathcal{O}(1)\)

二维差分

类比 \(S_{i,j}=S_{i-1,j}+S_{i,j-1}-S_{i-1,j-1}+A_{ij}\)

因为 \(B\) 的前缀和为 \(A\),则用 \(A\) 替代 \(S\),\(B\) 替代 \(A\) 则

\[A_{i,j}=A_{i-1,j}+A_{i,j-1}-A_{i-1,j-1}+B_{i,j}\\ B_{ij}=A_{i,j}-A_{i-1,j}-A_{i,j-1}+A_{i-1,j-1} \]

这就是二维差分的初始化了

我们考虑修改 \((u,v)\) 到 \((n,n)\) 的差分

  • 当 \((i,j) = (u,v)\) 时,\(B'_{i,j}=(A_{i,j}+w)-A_{i-1,j}-A_{i,j-1}+A_{i-1,j-1}=B_{i,j}+w\)

可以证明,其他位置的差分值不变

可以推导 \((u,v)\) 到 \((x,y)\) 的差分值为

\[B_{u,v}+w,B_{u,y+1}-w,B_{x+1,v}-w,B_{x+1,y+1}+w \]

证明平凡,类似后缀和

多维差分

和二维差分的推导过程一样

\(\mathcal{Practice}\)

见题单

\(\mathcal{Part}\) 5.离散化

我们要将一个值域为 \((-\infty,\infty)\) 的数映射到 \((0,n]\) 之中

怎么做到呢?

我们知道,映射有个性质

  • 每个数的大小关系不变

想到什么?排名是不是可以完美做到这一点?

所以,我们第一步是将原数组去重排序+排序

排序用 sort 人尽皆知,去重用什么呢?

使用 unique 函数,使用格式: unique(a.begin(),a.end())这里 a.begin(),a.end() 分别指数组的头指针和尾指针,普通数组用 a+1,a+1+n

  • 注意一点:该函数的返回值是不属于去重数组的第一个地址,比如有个数组长度为 \(n\) ,去重数组长度为 \(k\) ,他的返回值就是 \(a_{k+1}\)的地址。

根据上面几点,我们可以推出公式。

k=unique(a+1,a+1+n)-(a+1)

这样我们就可以求出去重数组的长度了。

第二步,我们求排名

设这个数为 \(A_i\),\(B\) 数组的排名为 \(k\),则他在 \(A\) 数组的排名为 \(k\)

那怎么找 \(i\) 呢?这里使用一个函数:

lower_bound(a+1,a+1+n,x) 他返回的是第一个大于等于 \(x\) 的元素的地址,注意:必须有序

于是,我们可以推出公式:

rk[i] = lower_bound(b + 1, b + 1 + k, a[i]) - b;

到此结束

因为 \(rk_i\) 存的是第 \(a_i\) 在 \(b\) 数组的下标,则

\[B_{rk_i}=A_i \]

\(\mathcal{Practice}\)

见题单

\(\mathcal{Part}\) 6. 位运算

基本位运算

  • 按位与:\(x\&y\),把两数的对应位做逻辑与
  • 按位或:\(x|y\),把两位的对应位做逻辑或
  • 按位异或:\(x\oplus y\),把两位对应位做逻辑异或
  • 按位取反:\(!x\),把 \(x\) 的每一位取逻辑非
  • 左移:\(x<<y\),把 \(x\) 的整体向左移动 \(y\) 位,高位抹去,低位补0,(\(x<<y=x*2^{y-1}\))
  • 右移:\(x>>y\),把 \(x\) 的整体向右移动 \(y\) 位,低位抹去,高位补0,(\(x>>y=\cfrac{x}{2^{y-1}}\))(向下取整)

单点修改:

  • 将第 \(i\) 位修改为 \(1\):x |= 1<<(i-1)
  • 保证第 \(i\) 位为 \(1\),修改为 \(0\):x^=1<<(i-1)

注意:\(<<\) 或 \(>>\) 的优先级比 \(-\) 低,则:\(1<<(i-1)=1<<i-1\)

求两个集合的交集,并集和对称差

可以把两个集合看成一个二进制数,则

\(x_i=0\) 表示 \(i\) 不在集合中,反之同理

两个二进制的与,或,异或可以达到需要效果

神器!\(bitset\)

用二进制表示集合时,普通的变量存不下这么大的二进制,我们可以使用 \(bitset\) 创建一个超长二进制

支持:所有位运算操作也支持修改这个串的某一位

时间复杂度为:\(\mathcal{O}(\cfrac{n}{w})\),\(w\) 通常取 \(64\)

基本用法:

  • bitset<N> s 表示创建一个二进制数
  • s.set(i,0/1) 表示在第 \(i\) 位设置成 \(0/1\)
  • s.test(i) 返回第 \(i\) 位的值
  • s.count() 返回 1 的数量
  • s.reset() 把 \(s\) 清空成 0
  • s.flip() 对二进制取反
  • 可以直接进行逻辑运算

注意:声明 \(bitset\) 时,\(N\) 必须是常量(CE不关我事(doge.

\(\mathcal{Practice}\)

见题单

\(\mathcal{Part}\) 7. 单调数据结构

单调栈

单调栈维护的是一个栈,里面的元素单调递增或单调递减

用于找后缀最大值。

定义:当 \(i\) 为后缀最大值时,当且仅当:对于所有的 \(i<j\le|A|\),都有 \(A_i>A_j\)

因为栈内数据单调递减,所以对于一个在栈内的元素 \(A_i\),他后面没有比他大的数(不然就被弹出了

所以留在栈内的都是后缀最大值

时间复杂度为:\(\mathcal{O}(n)\)

单调队列

单调队列维护的是一个队列,只不过里面的数的下标都严格在一个区间里

比你小的人都打不过,你应该出队

被单调队列了

$ Practice$

题比较难,所以我写了几篇题解,自己去看

\(\mathcal{P}art\) 8.倍增

倍增基本概念

我们先来考虑一个引论

  • 任何一个 \(x\in \mathbb{N^*}\),都可以拆成若干个 \(2\) 的正整数次幂之和

\[n=\sum 2^i (i\in\mathbb{N^*}) \]

证明平凡

所以对于每一段区间,我们都可以拆成 \(\log n\) 个区间

倍增快速幂

我们要求 \(x^y \mod p\) 的值

\(x,y\le10^9\)

怎么办呢?

我们考虑使用倍增的思路:

\[x^y=(x^2)^{\frac{y}{2}} \]

这里需要进行奇偶性判断,如果是奇数,则直接乘进答案中即可

时间复杂度为:\(\mathcal{O}(\log x)\)

\(\mathcal{Code}\)

typedef long long ll;
ll qpow(ll d, ll b, ll Mod) {
    ll ans = 1;
    while (b) {
        if (b & 1) ans = ans * d % Mod;
        d >>= 1; b = b * b % Mod;
    }
    return ans;
}

暂更

标签:le,思想,sum,差分,算法,数组,mathcal,指针
From: https://www.cnblogs.com/Phrvth/p/17289620.html

相关文章

  • 自动驾驶-预瞄-Pure pursuit纯跟踪算法-MATLAB实现
    有空把引入、逻辑、原理介绍给写了,目前先给大家看看代码。将来写大概会分成这么几块:汽车运动学自行车模型跟踪算法主流模型及特点纯跟踪算法原理推导代码介绍代码原创,来之不易,请勿不注明转载。喜欢点个赞吧!网上许多代码都跑不起来hhclc;clear;%------------formroa......
  • 由数据范围反推算法复杂度以及算法内容
    由数据范围反推算法复杂度以及算法内容1、一般ACM或者笔试题的时间限制是1秒或2秒。C++里面如果题目的时间限制是1s的话,这个1s是指每一个测试数据都有1s的时间限制,如果一个题有十几个测试数据,每一个测试数据都有1s的实现,正常比赛的话,比如蓝桥杯比赛的话,如果有10个测试数据,时间......
  • AcWing算法提高课-1.1.1摘花生
    题目描述HelloKitty想摘点花生送给她喜欢的米老鼠。她来到一片有网格状道路的矩形花生地(如下图),从西北角进去,东南角出来。地里每个道路的交叉点上都有种着一株花生苗,上面有若干颗花生,经过一株花生苗就能摘走该它上面所有的花生。HelloKitty只能向东或向南走,不能向西或向北......
  • JVM的垃圾收集算法
    介绍分代收集理论和几种垃圾收集算法的思想及其发展过程。分代收集理论当前商业虚拟机的垃圾收集器,大多数都遵循了“分代收集”(GenerationalCollection)的理论进行设计,分代收集名为理论,实质是一套符合大多数程序运行实际情况的经验法则,分代收集理论它建立在两个分代假说之上:弱......
  • python机器学习案例系列教程——K最近邻算法(KNN)、kd树
    全栈工程师开发手册(作者:栾鹏)python数据挖掘系列教程K最近邻简介K最近邻属于一种估值或分类算法,他的解释很容易。我们假设一个人的优秀成为设定为1、2、3、4、5、6、7、8、9、10数值表示,其中10表示最优秀,1表示最不优秀。我们都知道近朱者赤,近墨者黑,我们想看一个人是什么样的,看......
  • STAB算法
    SATB算法思想简介SATB算法的基本思想,可以概括为如下三句话:并发标记之前先给Region内存打个快照,标记线程基于这个快照独立进行标记。应用线程不会直接修改这个快照中的对象,也就是说应用线程不会干扰标记线程的工作。应用线程新分配的对象都认为是活跃对象,实际在下一个并发标记周......
  • JVM的垃圾收集算法
    介绍分代收集理论和几种垃圾收集算法的思想及其发展过程。分代收集理论当前商业虚拟机的垃圾收集器,大多数都遵循了“分代收集”(GenerationalCollection)的理论进行设计,分代收集名为理论,实质是一套符合大多数程序运行实际情况的经验法则,分代收集理论它建立在两个分代假说之上:......
  • m基于多核学习支持向量机MKLSVM的数据预测分类算法matlab仿真
    1.算法描述        20世纪60年代Vapnik等人提出了统计学习理论。基于该理论,于90年代给出了一种新的学习方法——支持向量机。该方法显著优点为根据结构风险最小化归纳准则,有效地避免了过学习、维数灾难和局部极小等传统机器学习中存在的弊端,且在小样本情况下仍然具有......
  • 笔记1. O(NlogN)的排序算法
    目录准备工作递归行为——求数组的最大值master公式归并排序——912.排序数组Merge函数归并排序主函数nlogn与n^2排序本质差距小和问题剑指Offer51.数组中的逆序对快排荷兰国旗问题快排1.0快排2.0快排3.0准备工作打印数组voidPrintfNums(int*nums,intnumsSize){......
  • m基于多核学习支持向量机MKLSVM的数据预测分类算法matlab仿真
    1.算法描述20世纪60年代Vapnik等人提出了统计学习理论。基于该理论,于90年代给出了一种新的学习方法——支持向量机。该方法显著优点为根据结构风险最小化归纳准则,有效地避免了过学习、维数灾难和局部极小等传统机器学习中存在的弊端,且在小样本情况下仍然具有良好的泛化能力,从......