复杂度
修改和查询的时间复杂度都降到
长啥样?
性质
每个父节点都是他的两个子节点值的和
咋用
看看这个图,补上几个子节点,发现就会变成 完全二叉树
那么,我们就可以直接用数组来存储这个玩意。
根节点编号为 n ,那么它的左子树编号为 2n , 它的右子树编号为 2n + 1 。
准备
#include <iostream>#include <cstring>#include <algorithm>using namespace std;#define ll long longconst int N = 100010;ll arr[N] = {0},tree[4*N] = {0};//arr 为存储数据的数组, tree 是存线段树的数组
创造数组这里要注意一下,一般为了防爆,线段树数组空间要开数据数组的四倍
建树
build
void build(int node, int start, int end){//建树函数,参数分别为 根节点、左右区间范围if(start == end){//如果两边相等,说明已经到了叶节点tree[node] = arr[start];//根节点就填入数据数组的值return;//递归边界}//依据完全二叉树的性质得到左右子节点编号int leftnode = node * 2;int rightnode = node * 2 + 1;//从中间分为两份,左右分别建树int mid = (start + end) / 2;build(leftnode, start, mid);build(rightnode, mid + 1, end);//根节点的值 = 左根节点的值 + 右根节点的值tree[node] = tree[leftnode] + tree[rightnode];}
更新树
update
//修改操作,参数分别为根节点、左右区间范围、要修改的节点的编号、要改为多少的值void update(int node, int start, int end, int id, int val){if(start == end){//递归边界,找到叶节点tree[node] += val;//修改 node 节点的值return;}int leftnode = node * 2;int rightnode = node * 2 +1;int mid = (start + end) / 2;if(id >= start && id <= mid)//如果要改的地方在左半边update(leftnode, start, mid, id, val);else //否则update(rightnode, mid + 1, end, id, val);tree[node] = tree[leftnode] + tree[rightnode];//父节点更新}
区间求和
query
//求和 [l,r] 区间int query(int node, int start, int end, int l, int r){//要求和的区间已经包含当前的部分了/*例子:如 当前在 [1,3], 要求 [1,5], 则需要[1,3] + [4,5]那么就直接返回建树时算好的 [1,3] 的和*/if(l <= start && r >= end)return tree[node];int leftnode = node * 2;int rightnode = node * 2 + 1;int mid = (start + end) / 2;int sum = 0;//待会要返回的 和if(l < mid) //如果要求和的区间包含中点左边的部分sum += query(leftnode, start, mid, l, r);else //否则sum += query(rightnode, mid + 1, end, l, r);return sum;}
题目
01
有 2^n(n\le7)2n(n≤7) 个国家参加世界杯决赛圈且进入淘汰赛环节。我经知道各个国家的能力值,且都不相等。能力值高的国家和能力值低的国家踢比赛时高者获胜。1 号国家和 2 号国家踢一场比赛,胜者晋级。3 号国家和 4 号国家也踢一场,胜者晋级……晋级后的国家用相同的方法继续完成赛程,直到决出冠军。给出各个国家的能力值,请问亚军是哪个国家? 输入输出样例: 输入 3 4 2 3 1 10 5 9 7 输出: 1 —-洛谷 | P4715 【深基16.例1】淘汰赛
分析题目可知,我们可以用一个线段树—根节点装的是两个子节点的最大值,冠军肯定是 tree[2] 和 tree[3]中的一个,亚军就是其中较小的一个
红色为 线段树数组编号
#include <iostream>#include <cstring>#include <algorithm>using namespace std;#define ll long longconst int N = 155;struct Team{int id,ability;//队伍序号和能力值};Team maxt(Team a, Team b){return a.ability > b.ability? a:b;}Team mint(Team a, Team b){return a.ability < b.ability? a:b;}Team a[N],tree[4*N];void build(int node, int start, int end){if(start == end){tree[node] = a[start];return;}int ln = node * 2, rn = node * 2 + 1;int mid = (start + end) / 2;build(ln, start, mid);build(rn, mid + 1, end);tree[node] = maxt(tree[ln], tree[rn]);}int main(){int n;cin>>n;for(int i = 1; i <= (1<<n); ++i){cin>>a[i].ability;a[i].id = i;}build(1,1,(1<<n));cout<<mint(tree[2],tree[3]).id;return 0;}
相信你看了上面的内容,看这个应该没有问题。
总结
如有疏漏,欢迎指正。
