清北学堂noip2018集训D2

P.S.

今天讲数据结构,大概包括了二叉堆,二叉搜索树,线段树,树状数组,并查集,st表,RMQ,LCA。

二叉堆

  • 基本功能

    1. O(log n)插入元素
    2. O(1)查最大(最小)元素
    3. O(log n)删除最大(最小)元素
    4. O(log n)删除任意元素
  • 基本结构
    • 最为常用的堆结构就是完全二叉堆。
    • 在逻辑形式上,结构必须等同于完全二叉树,同时,堆顶以外的每个节点都不大于(小于)其父亲,即堆有序。
    • 如果堆顶最大,称为最大二叉堆(大根堆),反之称为最小二叉堆(小根堆)。
    • 因为完全二叉堆等同于完全二叉树的结构,故高度相当。为O(log n).
    • 一般为了实现方便,根节点编号为1,节点n左节点为2n,右节点为2n+1.
查询操作
  • 最大(最小),直接返回堆顶即可。
插入操作
  • 首先把新元素插入向量末尾,为了维持堆的有序性,逐层检查是否有序并调整,过程通常称为上滤。
建堆
  • O(n log n)插入每个元素即可。
  • O(n)?
    • 先随机放入一个完全二叉树。
    • 从倒数第二个深度开始检索,使堆有序。
删除最大(最小)元素
  • 首先将堆顶元素与堆尾元素交换。
  • 删除堆尾元素。
  • 一步一步将这个换上去的元素下移。
  • 直到堆有序。
删除任意元素
  • 新建一个堆,将删除的元素放入新堆里。即可实现删除。

二叉搜索树

  • 基本功能

    • 排序
    • 查询元素大小排名
    • 插入元素
    • 二叉平衡树的基础
  • 结构
    • 若左子树不为空,则左子树上所有节点小于根节点。
    • 若右子树不为空,则右子树上所有节点大于根节点。
    • 左右子树也分别称二叉排序树。
  • 排序
    • 中序遍历
  • 查询元素大小排次
    • 从根开始向左、右是唯一的,如果向右,那么将左边的节点数加起来,反之不加。
  • 插入
    • 与查询方法类似。
    • 查询到节点后向上更新其他节点。

线段树

  • 基本功能

    • 建树
    • 单点查询
    • 单点修改
    • 区间查询
    • 区间修改
  • 结构
    • 是一棵二叉搜索树,每个节点代表一个区间。
    • 每个节点需要维护
      • 区间左右端点
      • 区间要维护的信息
  • 基本思想:二分
  • 特殊性质
    • 左子节点区间为[l,mid],右为[mid+1,r].
    • 对于一个节点k,左子节点为2k,右子节点为2k+1.
  • 建树
    • 对于二分的每一个节点,确定其代表的区间。
    • 如果为叶子结点,存需要维护的信息。
    • 对于非叶子结点,将两个子节点状态合并。
  • 单点查询
    • 在左右节点搜索是确定的,由节点的mid决定,如果要查询的点大于mid,则在右子树搜索,反之在左子树搜索。代码如下:

      #include<bits/stdc++.h>
      using namespace std;
      #define LL long long
      const int MAXN = 100000;
      struct tree{int l,r,s;
      }mi[4*MAXN+20];
      void build(int k,int l,int r){mi[k].l=l;mi[k].r=r;if(l==r){mi[k].s=r;return;}int mid=(l+r)/2;build(2*k,l,mid);build(2*k+1,mid+1,r);mi[k].s=mi[k*2].s+mi[k*2+1].s;
      }
      int search(int k,int n){if(mi[k].l==mi[k].r&&mi[k].r==n){return k;}int mid=(mi[k].l+mi[k].r)/2;if(n<=mid) return search(k*2,n);else return search(k*2+1,n);
      }
      int main(){build(1,1,6);cout<<search(1,5);return 0;
      }
      
  • 单点修改
    • 先执行单点搜索,然后执行修改操作,再向上更新状态。
    • 代码如下
    int insert(int k,int n,int v){int now=search(k,n);mi[now].s+=v;while(now!=1){now/=2;mi[now].s=mi[2*now].s+mi[2*now+1].s;}
    }
    
  • 区间查询
    • 令[l,r]为当前节点代表的区间,mid代表区间中点,[x,y]代表要查询的区间。
    • 若[l,r]=[x,y]直接返回即可。
    • 如果y≤midy\leq midy≤mid,[l,r]->[l,mid],[x,y]不变,递归查询。
    • 如果x&gt;midx &gt; midx>mid,[l,r]->[mid+1,r],[x,y]不变,递归查询。
    • 否则,[x,y]跨过了mid的两端,将其分成了两个子问题,[l,mid][l,mid][l,mid]中查[x,mid][x,mid][x,mid].[mid+1,r][mid+1,r][mid+1,r]中查[mid+1,y][mid+1,y][mid+1,y].
    • 代码如下:
    int query_sum(int k,int x,int y){if(y<mi[k].l||x>mi[k].r) return 0;if(x==mi[k].l&&mi[k].r==y) return mi[k].s;int mid=(mi[k].l+mi[k].r)/2;return query_sum(k*2,x,mid)+query_sum(k*2+1,mid+1,y);
    }
    
  • 区间修改
    • 关键:懒标记
    • 经过节点的集合与区间查询相同
    • 更新这些区间的答案并打上一个标记,来表示这个区间中的数要进行哪些修改。
    • 只有当查询或者修改经过一个节点时,才将其节点上的懒标记放到两个孩子节点,下放的同时修改两个孩子节点的答案。

模板(无区间修改)

/*求和线段树*/
#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define inf 2147483647
const int MAXN = 100000;
struct tree{int l,r,s;
}mi[4*MAXN+20];
void build(int k,int l,int r){mi[k].l=l;mi[k].r=r;if(l==r){mi[k].s=r;return;}int mid=(l+r)/2;build(2*k,l,mid);build(2*k+1,mid+1,r);mi[k].s=mi[k*2].s+mi[k*2+1].s;
}
int search(int k,int n){if(mi[k].l==mi[k].r&&mi[k].r==n){return k;}int mid=(mi[k].l+mi[k].r)/2;if(n<=mid) return search(k*2,n);else return search(k*2+1,n);
}
int query_sum(int k,int x,int y){if(y<mi[k].l||x>mi[k].r) return 0;if(x==mi[k].l&&mi[k].r==y) return mi[k].s;int mid=(mi[k].l+mi[k].r)/2;return query_sum(k*2,x,mid)+query_sum(k*2+1,mid+1,y);
}
int insert(int k,int n,int v){int now=search(k,n);mi[now].s+=v;while(now!=1){now/=2;mi[now].s=mi[2*now].s+mi[2*now+1].s;}
}
int main(){int n,m,x,v;cin>>n>>m>>x>>v;build(1,n,m);cout<<query_sum(1,x,v);return 0;
}

树状数组

  • 基本功能

    • 单点修改,前缀信息查询。
    • 区间修改可减信息(前缀和),单点查询。
  • lowbit函数
    • 含义:一个数,二进制表示中的最后一个1。
    • 计算机中负数的表示?
    • 怎么求lowbit呢?
    • xand(−x)x and (-x)xand(−x)
  • 结构
    • 我们定义c[i]为区间[i−lowbit(i)+1,i][i-lowbit(i)+1,i][i−lowbit(i)+1,i]。
    • 怎么直观的看一看呢?

    • Sn=S(n−lowbit(n))+CnS_n=S_{(n-lowbit(n))}+C_nSn​=S(n−lowbit(n))​+Cn​
  • 查询代码
int sum(int x){int ans=0;for(;x;x-=lowbit(x)) ans+=c[x];return ans;
}
  • 修改代码
void update(int x,int v){for(;x<=n;x+=lowbit(x)) c[x]+=v;
}
  • 代码示例:
#include<bits/stdc++.h>
using namespace std;
const int maxn=100010;
int a[maxn],c[maxn];
int n;
int lowbit(int x){return x&(-x);
}
int sum(int x){int ans=0;for(;x;x-=lowbit(x)) ans+=c[x];return ans;
}
void update(int x,int v){for(;x<=n;x+=lowbit(x)) c[x]+=v;
}
int main(){cin>>n;for(int i=1;i<=n;i++){cin>>a[i];update(i,a[i]);}cout<<sum(n);return 0;
}
  • 区间修改,单点查询

    • 且以修改为加法,查询为求和为例。
    • 对于将[l,r][l,r][l,r]加xxx的操作,可以将点lll加xxx,将点r+1r+1r+1减xxx,这时候求点nnn的前缀和就能得到点nnn真实的值。

并查集

  • 基本功能

    • 合并两个集合
    • 查询某一个元素属于哪个集合
  • 结构
    • 并查集S由若干子集合SiS_iSi​构成,并查集的逻辑结构是一个森林。
    • SiS_iSi​表示森林中的一颗子树。
    • 一般以子树的根作为该子树的代表。
    • 而对于并查集的存储结构,可用一维数组来实现。
  • 查询某个元素属于哪个集合
    • 通常而言都是以根作为这个集合的代表元。
    • 因此只需要不断向父亲节点走,直到走到根为止返回即可。
  • 合并集合
  • 路径压缩优化
  • 示例代码
    #include<bits/stdc++.h>
    using namespace std;
    int fa[200010];
    int n;
    int find(int x) {if(fa[x] == 0) return x;return fa[x] = find(fa[x]);
    }
    void add(int x,int y){int xx=find(x),yy=find(y);fa[yy]=xx;
    }
    int main(){cin>>n;int x,y,m,z;for(int i=0;i<n;i++){cin>>x>>y;add(x,y);}cin>>m>>z;cout<<find(m)<<" "<<find(z);return 0;
    }
    

RMQ问题

  • R——Range
  • M——Minimum/Maximum
  • Q——Query
  • 即区间最值问题

例题

  • 方法1:使用线段树
  • 方法2:st表

ST表

  • 基本功能

    • O(nlogn)O(nlogn)O(nlogn)预处理,O(1)O(1)O(1)查询区间最值。
  • 结构
    • f[i][j]f[i][j]f[i][j]表示区间[i,i+2j−1][i,i+2^j-1][i,i+2j−1]的信息。
    • 整个fff数组就是ST表。
  • 建表
    • f[i][0]f[i][0]f[i][0]就是单点i的信息
    • 当j&gt;0j&gt;0j>0时,f[i][j]f[i][j]f[i][j]为f[i][j−1],f[i+2j−1−1][j−1]f[i][j-1],f[i+2^{j-1}-1][j-1]f[i][j−1],f[i+2j−1−1][j−1]两个区间信息的并集。
  • 查询
    • 比方说我们要查询区间[x,y][x,y][x,y]的信息。
    • 令t为最大的正整数使得2t≤y−x+12^t\leq y-x+12t≤y−x+1
    • 那么答案就是[x,x+2t−1]∪[y−2t+1,y][x,x+2^t-1]\cup[y-2^t+1,y][x,x+2t−1]∪[y−2t+1,y]
  • 代码如下:
#include<bits/stdc++.h>
#define maxn 111100
#define logN 25
//因为cmath中的log函数效率差,不如直接设定一个永远到不了的值
using namespace std;int f[maxn][logN],a[maxn],n,m; void pre_st(){               //制表 for(int i=1;i<=n;i++)f[i][0]=a[i];     //因为第二个框框中存的j是用来计算 i+2^j-1(既该f保存的值) //服务于动态规划的结果 for(int j=1;j<=logN;j++){for(int i=1;i+(1<<j)-1<=n;i++)f[i][j]=max(f[i][j-1],f[i+(1<<j-1)][j-1]);          //注释1 (1<<j)是计算出 2^j 把一一直右移即可得到//注释2  使用刚才得到的动态规划方程 }
}int main(){cin >> n;cin >> m;for(int i=1;i<=n;i++) scanf("%d",&a[i]);pre_st();                            //制表        int x,y;        for(int i=1;i<=m;i++){   cin >> x >> y;int s=log(y-x+1)/log(2);      //计算出这一段距离之中最大的2的倍数,以查表 cout << max(f[x][s],f[y-(1<<s)+1][s]) << endl;;     //合并左右不分的解 }return 0;
} 

查询树上两点的最近公共祖先(LCA)

  • L——Lowest
  • C——Common
  • A——Ancestor
  • 即最近公共祖先

欧拉序

  • 一种特殊的dfs序,每到达一个点就加入序列。
  • 记录每个点第一次出现的时间S[u]S[u]S[u],欧拉序中第i个点为Q[i]Q[i]Q[i]。
  • LCA(x,y)LCA(x,y)LCA(x,y)是Q[S[x]...S[y]]Q[S[x]...S[y]]Q[S[x]...S[y]]中层数最低的点。
  • 区间最值?
  • ST表!

清北学堂noip2018集训D2相关推荐

  1. 清北学堂noip2019集训D2——数据结构

    基本数据结构 栈 STL:stack 定义:stack a; 查询堆顶:a.top(); 压入栈顶:a.pop(); 查询a中的元素个数:a.size(); 清空只能慢慢pop. 例题1 给定一个栈, ...

  2. 清北学堂----北京集训

    7月16日 集训第一天,毛晗杨给我们讲的基础算法和数论.基础算法讲了分块和三分等知识点,分块之前接触过,个人认为和莫队有一定的相似之处,都是把整个数组分为一个个大小相等的块,然后对块进行操作,每个块大 ...

  3. 清北学堂noip2019集训D6——动态规划

    状态压缩DP 最短哈密顿回路问题 给定一个完全图,带正边权w(u,v)w(u,v)w(u,v).求出一个顶点的排列v1,v2,...,vnv_1,v_2,...,v_nv1​,v2​,...,vn​, ...

  4. 清北NOIP训练营集训笔记——图论(提高组精英班)

    清北NOIP训练营集训笔记--图论(提高组精英班) 本文摘自清北学堂内部图论笔记,作者为潘恺璠,来自柳铁一中曾参加过清北训练营提高组精英班,笔记非常详细,特分享给大家!更多信息学资源关注微信订阅号no ...

  5. 2020清北学堂秋季营感想——Hoarfrost

    2020清北学堂秋季营感想 前言:九月三十日放假以后,就马不停蹄地开始了这一次的奥赛培训.原先参加过暑假的提高组腾飞营,当时第一场模拟赛拿了第一,便觉得CSP的题目难度不会很高,普及+/提高-左右的难 ...

  6. 2017国庆 济南清北学堂 8天乐

    Day 0 想吐槽济南的堵车 大约接近4点从莱芜走的,走高速一个多小时,5点左右就到了济南,但是因为济南的堵车,下班高峰期,用了两个多小时才到了我们的目的地.好不容易到宾馆登记了,mc还要我们开会,8 ...

  7. 清明培训 清北学堂 DAY1

    今天是李昊老师的讲授~~ 总结了一下今天的内容: 1.高精度算法 (1)   高精度加法 思路:模拟竖式运算 注意:进位 优化:压位 程序代码: #include<iostream> #i ...

  8. 清北学堂 2017-10-07

    ********今天得了在清北的最高分,有点开心wwwww,mjy爱您! 树  [问题背景]  zhx 是二叉查找树大师.  [问题描述]  二叉查找树是一种特殊的二叉树(每个节点最多只有两个儿子的树 ...

  9. 【十一日记】 清北学堂酱油记

    Day 0 五点起床 困~ 喜闻乐见的六点集合 嗯我们出发的时候迟到了四分钟( 06:26 第一次出去怎么多人 啧啧啧 @57的熊孩子们好多啊qwq 十分钟到一中 果然老司机233 嗯 目睹他们放学吃 ...

  10. 2017清北学堂(提高组精英班)集训笔记——基础算法

    我这更新笔记的顺序有点乱时间也很乱,见谅,(其实我是想偷懒什么简单先更什么O(∩_∩)O~) 一.倍增算法: 定义:用f[i][j]表示从i位置出发的2j个位置的信息综合(状态) 一个小小的问题:为什 ...

最新文章

  1. JS动态生成的元素,其对应的方法不响应(比如单击事件,鼠标移动事件等)...
  2. 多线程-多图下载综合案例-SDWebImage框架实现代码
  3. 生成android_Android开发:生成桌面快捷方式是这样做的
  4. c语言for循环说课稿,C语言FOR循环说课稿.docx
  5. layui表格弹窗修改_layUI 实现自定义弹窗
  6. SQL server经典电子书、工具和视频教程汇总
  7. Java数据结构-基于数组的栈和泛型
  8. Redis集群明细文档(转)
  9. 滚屏加载--无刷新动态加载数据技术的应用
  10. linux centos7 cuda安装
  11. 常见 Java 异常解释(恶搞版)
  12. Oracle触发器3-DDL触发器
  13. 2019-06-02 Java学习日记之多线程上
  14. NF、$NF、FR、FNR举例说明
  15. 如何学好Python自动化,每个进阶的测试人都应该看看
  16. CKEditor4x word导入不保存格式的解决方案
  17. 【STM32H7的DSP教程】第5章 Matlab简易使用之常用编程语句
  18. 计算机应用软件论文范文,计算机论文范文大全集(计算机应用论文范文5000字)
  19. 数商云:分享国内快消品B2B平台的仓储物流模式
  20. P3224 [HNOI2012]永无乡 FHQ-Treap 启发式合并

热门文章

  1. 电脑开热点手机无法连接,无法启用 Internet 连接共享.为 LAN 连接配置的 IP 地址需要使用自动 IP 寻址.
  2. 分享几个简单的HTML网页特效代码
  3. 卸载计算机系统,W7电脑系统如何卸载软件
  4. 怎么查看计算机mac地址,怎么查电脑的mac地址?电脑mac地址查询方法
  5. 庆祝61-牛客模拟笔试七月场(使圆圈队形中相邻小朋友的身高差的最大值最小的解法)
  6. python图片爬虫,指定关键字爬取Google图片+搜狗图片
  7. 英文邮件中常见的英语缩写
  8. 硬盘分区被格式化了如何恢复
  9. Ubuntu下deb包的安装方法
  10. 微信公众号获取永久素材