引入

题目描述题目描述题目描述

100001000010000个正整数,编号从111到100001000010000,用A[1]A[1]A[1],A[2]A[2]A[2],A[10000]A[10000]A[10000]表示。
修改:1.将第 pospospos 个数增加 valvalval (1<=L<=10000)(1 <= L <= 10000)(1<=L<=10000)
统计:1.编号从 LLL 到 RRR 的所有数之和为多少? 其中 1<=L<=R<=10000.1<= L <= R <= 10000.1<=L<=R<=10000.

方法如下(目前所学):

  1. 修改 A[pos]A[pos]A[pos] ——O(1)O(1)O(1) ;循环求和 ——O(n)O(n)O(n)
  2. 差分:修改 nnn 个元素 O(n)O(n)O(n) ; 令 S[0]=0, S[k]=A[1…k] ,那么,A[L…R]的和就等于S[R]-S[L-1] ———O(1)O(1)O(1)

从上可以看出 方法一修改快,求和慢。 方法二求和快,修改慢。
那有没有一种结构,修改和求和都比较快呢?答案当然是线段树线段树线段树

线段树的性质

众所周知, 线段树是一棵完全二叉树
设其中一子节点编号为 aaa , 则该节点左儿子编号为 2a2a2a , 其右儿子编号为 2a+12a+12a+1

另:线段树的子节点对应一个区间 , 顾名思义

线段树的算法

  1. 结构体(tree)
struct Tree
{int left,right,val=INT_MAX;
}tree[maxn<<2];//定义
inline void push_up(int )
{tree[o].val=min(tree[left].val,tree[right].val);//根据题目要求
}//向上调整
void build(int k,int l,int r)
{tree[k].left=l;tree[k].right=r;if(l==r){tree[k].val=read();//tree[k].val=a[l];return;}int mid=(l+r)/2;int left=k*2,right=k*2+1;build(left,l,mid);build(right,mid+1,r);push_up();
}//建树
  1. 多个数组
    函数需多加两个引用
int tree[maxn<<2];//定义
inline void build(int o, int l, int r)
{if (l == r){cin >> tree[o];return;}int mid = (l + r) >> 1;build(lw(o), l, mid);build(rw(o), mid + 1, r);push_up(o);
}//建图
inline void update(int o, int l, int r, int pos, int val);//单点更新
inline int query(int o, int left, int right, int l, int r)//区间查询//left和right是目标区间//l和r是节点区间(开始一般取1,n)

传送门

线段树的单点修改

1、分解区间

首先是讲原始子区间的分解,假定给定区间[L,R][L,R][L,R],只要L<RL < RL<R ,线段树就会把它继续分裂成两个区间。

首先计算 M=(L+R)/2M = (L+R)/2M=(L+R)/2,左子区间为[L,M][L,M][L,M],右子区间为[M+1,R][M+1,R][M+1,R],然后如果子区间不满足条件就继续递归分解。

分解条件:要赋值的位置在midmidmid左则进入区间[L,MID][L,MID][L,MID],否则进入区间[MID+1,R][MID+1,R][MID+1,R]

2、判断子区间是否左右相等

  • 若相等,则赋值,return
  • 若不相等,则继续分解区间分解区间分解区间(参考1),并向上调整(push_up)

线段树的区间查询

1、继续分解区间

2、判断当前区间是否位于目标区间之内(完全包含)

  • 若包含,则返回ans
  • 若不包含,则继续分解区间分解区间分解区间,并向上调整
 int sum1=INT_MAX,sum2=INT_MAX;if(L<=mid){sum1=query(left,L,R);}if(R>mid){sum2=query(right,L,R);}

最后sum1和sum2取最小值返回


模板

#include<bits/stdc++.h>
using namespace std;int read()
{int s=0; char c=getchar();for (;!isdigit(c);c=getchar());for (;isdigit(c);c=getchar()) s=s*10+c-'0';return s;
}const int maxn=1e5;
int n,m;
struct Tree
{int left,right,val=INT_MAX;
}tree[maxn*4+99];void build(int k,int l,int r)
{tree[k].left=l;tree[k].right=r;if(l==r){tree[k].val=read();return;}int mid=(l+r)/2;int left=k*2,right=k*2+1;build(left,l,mid);build(right,mid+1,r);tree[k].val=min(tree[left].val,tree[right].val);
}void update(int o,int pos,int val)
{if(tree[o].left==tree[o].right){tree[o].val=val;return;}int mid=(tree[o].left+tree[o].right)/2;int left=o*2,right=o*2+1;if(pos<=mid){update(left,pos,val);}else{update(right,pos,val);}tree[o].val=min(tree[left].val,tree[right].val);
}int query(int o,int L,int R)
{if(L<=tree[o].left&&R>=tree[o].right){return tree[o].val;}int mid=(tree[o].left+tree[o].right)/2;int left=o*2,right=o*2+1;int sum1=INT_MAX,sum2=INT_MAX;if(L<=mid){sum1=query(left,L,R);}if(R>mid){sum2=query(right,L,R);}return min(sum1,sum2);
}signed main()
{cin>>n>>m;build(1,1,n);//for(int i=1;i<=n;i++)cout<<summ[i]<<" ";//cout<<endl;for(int i=1;i<=m;i++){int q,l,r;q=read();l=read();r=read();if(q==2){update(1,l,r);}else{cout<<query(1,l,r)<<" ";}}
}

例题

逆序对

题目描述

猫猫 TOM 和小老鼠 JERRY 最近又较量上了,但是毕竟都是成年人,他们已经不喜欢再玩那种你追我赶的游戏,现在他们喜欢玩统计。
最近,TOM 老猫查阅到一个人类称之为“逆序对”的东西,这东西是这样定义的:对于给定的一段正整数序列,逆序对就是序列中 a[i]>a[j]且i<ja[i]>a[j] 且 i<ja[i]>a[j]且i<j
且 i<ji<ji<j 的有序对。知道这概念后,他们就比赛谁先算出给定的一段正整数序列中逆序对的数目。注意序列中可能有重复数字。

Update:数据已加强。

输入格式

第一行,一个数 nnn,表示序列中有 nnn 个数。
第二行 nnn 个数,表示给定的序列。序列中每个数字不超过 10910^9109 。

输出格式

输出序列中逆序对的数目。

输入 #1

6
5 4 2 6 3 1

输出 #1

11

思路

本题采用值域线段树值域线段树值域线段树

tree[o]=val;

tree[o]++;

意为统计某个数ooo出现的个数

  1. 先对数据进行离散化离散化离散化(lowerboundlower_boundlowerb​ound)
    eg.eg.eg.
    1 2 3 4 9 7
    1 2 3 4 6 5
  2. 再边记录边查询(查询比数 iii 大的数 jjj 出现的个数)
  3. ans累加

本人AC程序

#include <bits/stdc++.h>
using namespace std;#define int long long
#define lw(a) a << 1
#define rw(a) (a << 1) | 1const int maxn = 2e5 + 5;int n, a[maxn], b[maxn];
int tree[maxn << 2];
int ans;inline int read()
{char c = getchar();int x = 0;bool f = 0;for (; !isdigit(c); c = getchar()){f ^= !(c ^ 45);}for (; isdigit(c); c = getchar()){x = (x << 1) + (x << 3) + (c ^ 48);}if (f){x = -x;}return x;
}inline void write(int x)
{if (x < 0){putchar('-');x = -x;}if (x > 9){write(x / 10);}putchar(x % 10 + '0');
}inline bool comp(int x, int y)
{return b[x] > b[y];
}inline void push_up(int x)
{tree[x] = tree[lw(x)] + tree[rw(x)];
}inline void update(int o, int l, int r, int pos)
{if (l == r){tree[o]++;return;}int mid = (l + r) >> 1;if (pos <= mid){update(lw(o), l, mid, pos);}else{update(rw(o), mid + 1, r, pos);}push_up(o);
}inline int query(int o, int left, int right, int l, int r)
{if (left <= l && r <= right){return tree[o];}int mid = (l + r) >> 1;int ans = 0;if (left <= mid){ans += query(lw(o), left, right, l, mid);}if (right > mid){ans += query(rw(o), left, right, mid + 1, r);}return ans;
}signed main()
{n = read();for (int i = 1; i <= n; i++){b[i] = read();a[i] = b[i];}sort(b + 1, b + n + 1);for (int i = 1; i <= n; ++i){a[i] = lower_bound(b + 1, b + n + 1, a[i]) - b;}//for (int i = 1; i <= n; i++)//cout << a[i] << " ";//cout << endl;for (int i = 1; i <= n; i++){update(1, 1, n, a[i]);ans += query(1, a[i] + 1, n, 1, n);}write(ans);return 0;
}

线段树(单点修改,区间查询)相关推荐

  1. HDU1754 I Hate It (线段树单点修改+区间查询)

    题目链接:传送门 解题思路:从题目中我们很明显知道只有两种操作,第一种是查询\([A,B]\)范围内学生成绩的最大值,典型的RMQ,第二种操作是将A的成绩改为B,而不是改为B的成绩,请仔细体会.(当然 ...

  2. python:线段树区间修改 + 区间查询 模板 + 坑点总结

    from functools import reduceclass SegTree:'''支持增量更新,覆盖更新,序列更新,任意RMQ操作基于二叉树实现初始化:O(1)增量更新或覆盖更新的单次操作复杂 ...

  3. Ocean的礼物(线段树单点修改)

    题目链接:http://oj.ismdeep.com/contest/Problem?id=1284&pid=0 A: Ocean的礼物 Time Limit: 5 s      Memory ...

  4. CDOJ 1073 线段树 单点更新+区间查询 水题

    H - 秋实大哥与线段树 Time Limit:1000MS     Memory Limit:65535KB     64bit IO Format:%lld & %llu Submit S ...

  5. HDU 1698 Just a Hook (线段树区间修改+区间查询)

    题目链接: 传送门 题意:Pudge对装备钩子进行若干次的强化,强化分为三种分别对应的价值是1,2,3,在经历过若干次操作后,输出钩子对应的总价值,每次强化都是对钩子进行区间修改 解题思路:在明白了题 ...

  6. codeforces round #576 div2 D Welfare State(线段树)[单点修改+区间修改]

    题意:有一些数字,以及一些操作.操作一是单点修改,输入1 b c,将位置b改成c,操作二是输入2 a,将不大于a的数全部改成a.求更改完毕后的数. tag的运用:tag是对被覆盖区间上加一个标记,那么 ...

  7. tzoj3315 买火车票(线段树+区间修改+区间查询)

    时间限制(普通/Java):1000MS/3000MS     内存限制:65536KByte 描述 Byteotian州铁道部决定赶上时代,为此他们引进了城市联网.假设城市联网顺次连接着n 个市从1 ...

  8. HDU 1166 敌兵布阵(线段树单点加区间查询)

    Problem Description C国的死对头A国这段时间正在进行军事演习,所以C国间谍头子Derek和他手下Tidy又开始忙乎了.A国在海岸线沿直线布置了N个工兵营地,Derek和Tidy的任 ...

  9. 树链剖分+线段树 单点修改 区间求和 模板

    马上要去西安打邀请赛了,存下板子 首先是vector存图的: #include<bits/stdc++.h> using namespace std; #define ll long lo ...

  10. HDU1754 —— I Hate It 线段树 单点修改及区间最大值

    题目链接:https://vjudge.net/problem/HDU-1754 很多学校流行一种比较的习惯.老师们很喜欢询问,从某某到某某当中,分数最高的是多少.  这让很多学生很反感. 不管你喜不 ...

最新文章

  1. weblogic 部署后出现Error 404–Not Found
  2. C++ stirng,int 互转(转载)
  3. C++11 并发指南四(future 详解三 std::future std::shared_future)
  4. centOS7.6安装MySQL8
  5. jquery插件图片浏览
  6. npoi操作word书签_word进阶小教程,小研全部都给你!
  7. 047——VUE中css过渡动作实例
  8. Mysql - 巧用join来优化sql
  9. pm2 start 带参数_pm2 start命令进阶详解
  10. poco mysql 编译_Poco介绍及编译
  11. 深度:老年消费品细分领域潜藏爆品机会,老花镜市场6000万风险投资揭示老年消费升级背后的创新机会!
  12. 大数据Hive搭建部署常见报错信息原因
  13. 5G时代,玖玖星球云算链引领VR技术踏上新台阶
  14. VScode终端配置bash.git(默认),terminal.integrated.shell:windows路径查找不到前提下
  15. unity3D 音频播放
  16. 富文本编辑器simditor
  17. 全景影像转点云——只有鱼才能看见的真实
  18. unity2d开发工具包_2D游戏工具包简介:通过拖放学习Unity
  19. R语言里的点样式pch
  20. 简单分享下在公众号上微信优惠券怎么弄

热门文章

  1. C++课程设计--课程表
  2. 推荐优质大数据学习平台
  3. 用JS打印斐波那契数列
  4. 思科设备路由综合配置
  5. 【日常】我的扬马最后一小时
  6. 计算机保研陶瓷老师记录(1)
  7. java-php-python-ssm汽车租赁系统计算机毕业设计
  8. 汇编实验:DEBUG命令调试
  9. 计算机控制技术周俊,计算机控制技术
  10. 【源码】产生拉普拉斯分布的随机数