线段树例题

2024-05-28 14:36
文章标签 例题 线段

本文主要是介绍线段树例题,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

1.Sequence

2.Peach Conference

3.Permutation Subsequence


1.Sequence

题目描述:

Given an array a consisting of n integers, on which you are to perform m operations of two types.

1.Given two integers x,y, replace the number of index x with number y. That is ax:=y.

2.Given one integer x, print the number of consecutive subsequences of a, whose minimum value equals to ax.

It's guaranteed that there are no duplicated value in array a at any moment.

输入描述:

The first line contains two intergers n,m(1≤n,m≤10^5),where n is the size of the array and m is the number of operations to perform.

The second line contains n integer, the ith integer is ai (1≤ai≤2^31-1)

Then,m lines follow, describing m operation you are to perform in order.
Each line start with an integer opt∈[1,2], meaning the type of operation to perform.

If opt=1,two integers x, y (1≤x≤n,1≤y≤2^31-1) follows,mentioned above.

If opt=2,one integers x (1≤x≤n) follows, mentioned above.

输出描述:
For each operation of type 2, print one integer on one line as the answer.

输入样例:

10 5
8 3 6 2 10 9 5 7 1 4 
2 2
1 9 11
1 5 12
2 4
1 8 18

输出样例:

4
28

思路: 用线段树进行op=1的单点修改,用二分+区间查询进行op=2的查找Ax左边最后一个小于Ax的位置ans1,然后查找Ax右边第一个小于Ax的位置ans2,答案就是(x-ans1+1)*(ans2-x+1),这里的二分还是有许多细节需要注意,这里就不多说了,大家看代码理解吧

#include<iostream>
#include<limits.h>
#define int long long
using namespace std;
const int N=1e5+5;
int n,m,w[N];struct node{int l,r;int mind;
}tr[4*N];void pushup(int u)
{tr[u].mind=min(tr[u<<1].mind,tr[u<<1|1].mind);
}void build(int u,int l,int r)
{if(l==r)  tr[u]={l,l,w[l]};else{tr[u]={l,r};int mid=(l+r)>>1;build(u<<1,l,mid),build(u<<1|1,mid+1,r);pushup(u);}
}int query(int u,int l,int r)
{if(tr[u].l>=l&&tr[u].r<=r) return tr[u].mind;else{int mid=(tr[u].l+tr[u].r)>>1;int v=INT64_MAX;if(mid>=l) v=min(query(u<<1,l,r),v);if(mid<r) v=min(v,query(u<<1|1,l,r));return v;}
}void modify(int u,int x,int y)
{if(tr[u].l==x&&tr[u].r==x) tr[u]={x,x,y};else{int mid=(tr[u].l+tr[u].r)>>1;if(mid>=x) modify(u<<1,x,y);else modify(u<<1|1,x,y);pushup(u);}
}int32_t main()
{cin>>n>>m;for(int i=1;i<=n;i++) cin>>w[i];build(1,1,n);while(m--){int type;cin>>type;if(type==1){int x,y;cin>>x>>y;w[x]=y;modify(1,x,y);}else{int x;cin>>x;int a=w[x]; int l1=1,r1=x-1,ans1,ans2;while(l1<=r1)//先二分x的左边{int mid=(l1+r1+1)>>1;if(query(1,mid,x-1)<a) l1=mid+1;//如果mid~x-1这个区间存在比a更小的值,则区间向右边移动一个单位,缩小范围else r1=mid-1;//反之则扩大范围,向左边移动}ans1=l1;//取离x更近的值int l2=x+1,r2=n;while(l2<=r2)//二分x的右边{int mid=(r2+l2)>>1;if(query(1,x+1,mid)<a) r2=mid-1;//如果x+1~mid这个范围存在比a更小的值,则区间向左移动一个单位,缩小范围else l2=mid+1;//反之则扩大范围,向右边移动}ans2=r2;cout<<(x-ans1+1)*(ans2-x+1)<<endl;}}
}

2.Peach Conference

题目描述:

Sun Wukong was honored as the great saint of Qitian. He was very happy, so he held a peach meeting for the monkeys (ID numbered 1 to N). To make it more interesting, Sun Wukong decided to throw the dice. The conference will roll the dice Q times, forming Q instructions, each in the form of ’m a b’, where m is an integer label, and a and b are monkey’s ID. The meaning of each instruction is as follows:

1. If m > 0, send m peaches to each monkey in ID interval [a, b];

2. If m < 0, remove |m| peaches from each monkey in the ID interval [a, b] (if the number of peaches from any monkey is less than |m|, remove all peaches of the monkey);

3. If m = 0, calculate the sum of peaches of each monkey in the interval [a, b]; now you are invited to preside over the peach conference, can you complete the task according to the requirements of Sun Wukong?

输入描述:

The fifirst line contains two positive integers N and Q (1 ≤ N, Q ≤ 100000), representing N monkeys and Q instructions, respectively.

Next, there are Q lines, and each line corresponds to one instruction. Each instruction is composed of three integers m (−10000 ≤ m ≤ 10000), a and b (1 ≤ a ≤ b ≤ N), which respectively represent the label m and the ID interval of monkey.

输出描述:


Output each instruction with label m = 0 as an integer in order per line, that is, the sum of peaches of each monkey in the interval [a, b].

输入样例:

10 8
1 1 10
0 4 6
2 3 6
0 4 5
-2 5 8
0 4 7
-2 4 5
0 3 5

输出样例:

3
6
5
4

思路: 线段树+懒标记,如果在区间[l,r]中最小值减m会>=0,则不用进行标记,如果最大值减m<=0,则要进行标记,具体请看代码

#include<iostream>
#include<cstring>
#define int long long
using namespace std;
const int N=1e5+5;int n,q,w[N];
struct node{int l,r;int sum,mind,maxd,add,clear;
}tr[4*N];void pushdown(int u)
{node &left=tr[u<<1],&right=tr[u<<1|1],&root=tr[u];if(root.clear){left.sum=left.mind=left.maxd=left.add=0;left.clear=1;right.sum=right.mind=right.maxd=right.add=0;right.clear=1;root.clear=0;}if(root.add){left.sum+=(left.r-left.l+1)*root.add;left.mind+=root.add,left.maxd+=root.add;left.add+=root.add;right.sum+=(right.r-right.l+1)*root.add;right.mind+=root.add,right.maxd+=root.add;right.add+=root.add;root.add=0;return ;}}void pushup(int u)
{tr[u].sum=tr[u<<1].sum+tr[u<<1|1].sum;tr[u].maxd=max(tr[u<<1].maxd,tr[u<<1|1].maxd);tr[u].mind=min(tr[u<<1].mind,tr[u<<1|1].mind);}void Build(int u,int l,int r)
{if(l==r) tr[u]={l,l};else{tr[u]={l,r};int mid=(l+r)>>1;Build(u<<1,l,mid),Build(u<<1|1,mid+1,r);pushup(u);}
}void modify(int u,int l,int r,int d)
{if(tr[u].l>=l&&tr[u].r<=r) {if(tr[u].mind+d>=0){tr[u].sum+=(tr[u].r-tr[u].l+1)*d;tr[u].add+=d;tr[u].maxd+=d;tr[u].mind+=d;return ;}if(tr[u].maxd+d<=0){tr[u].sum=tr[u].mind=tr[u].maxd=tr[u].add=0;tr[u].clear=1;return ;}}pushdown(u);int mid=(tr[u].l+tr[u].r)>>1;if(mid>=l) modify(u<<1,l,r,d);if(mid<r) modify(u<<1|1,l,r,d);pushup(u);}int query(int u,int l,int r)
{if(tr[u].l>=l&&tr[u].r<=r)return tr[u].sum;else{pushdown(u);int mid=(tr[u].l+tr[u].r)>>1;int v=0;if(mid>=l)  v=query(u<<1,l,r);if(mid<r) v+=query(u<<1|1,l,r);return v;}
}int32_t main()
{cin>>n>>q;Build(1,1,n);while(q--){int m,l,r;cin>>m>>l>>r;if(m==0) cout<<query(1,l,r)<<endl;else  modify(1,l,r,m);}
}

3.Permutation Subsequence

注意 :要用线段树来优化找区间最值,不然会超时

代码:

#include<iostream>
#include<cmath>
using namespace std;
const int N=2e5+5;
int a[N],book[N],n,k;
struct node{int l, r;int maxd ,mind;
}tr[4*N];void pushup(int u)
{tr[u].maxd=max(tr[u<<1].maxd,tr[u<<1|1].maxd);tr[u].mind=min(tr[u<<1].mind,tr[u<<1|1].mind);
}void build(int u,int l,int r)
{if(l==r) tr[u]={l,l,book[l],book[l]};else {int mid=(l+r)>>1;tr[u]={l,r};build(u<<1,l,mid),build(u<<1|1,mid+1,r);pushup(u);}
}int query(int u,int l,int r,int type)
{if(tr[u].l>=l&&tr[u].r<=r) {if(type==1) return tr[u].mind;else return tr[u].maxd;}else{int mid=(tr[u].l+tr[u].r)>>1;if(type==1){int min_=0x3f3f3f3f;if(mid>=l) min_=query(u<<1,l,r,type);if(mid<r) min_=min(min_,query(u<<1|1,l,r,type));return min_;}else{int max_=0;if(mid>=l) max_=query(u<<1,l,r,type);if(mid<r) max_=max(max_,query(u<<1|1,l,r,type));return max_;}}
}
int main()
{cin>>n>>k;for(int i=1;i<=n;i++) {cin>>a[i];book[a[i]]=i;}int min_=0x3f3f3f3f;build(1,1,n);for(int i=1;i<=n-k+1;i++){int max_=query(1,i,i+k-1,2);int mi_=query(1,i,i+k-1,1);min_=min(min_,abs(max_-mi_));}cout<<min_;
}

这篇关于线段树例题的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1010839

相关文章

【数据结构与算法 经典例题】使用队列实现栈(图文详解)

💓 博客主页:倔强的石头的CSDN主页               📝Gitee主页:倔强的石头的gitee主页    ⏩ 文章专栏:《数据结构与算法 经典例题》C语言                                   期待您的关注 ​​ 目录  一、问题描述 二、前置知识 三、解题思路 四、C语言实现代码 🍃队列实现代码:

线段树单点修改的应用

思路:对初始状态进行建树,然后这题就相当于查询第一个合法的位置,并且对其值进行修改,整个题目要求维护的是区间最大值,很显然可以使用线段树。 #include <bits/stdc++.h>using namespace std;const int N = 2e6 + 5;typedef long long ll;typedef pair<ll, ll> pll;typedef arra

第9章 EM算法:例题及课后习题

1 概要 1.EM算法是含有隐变量的概率模型极大似然估计或极大后验概率估计的迭代算法。含有隐变量的概率模型的数据表示为 P ( Y , Z ∣ θ ) P(Y,Z|\theta) P(Y,Z∣θ)。这里, Y Y Y是观测变量的数据, Z Z Z是隐变量的数据, θ \theta θ 是模型参数。EM算法通过迭代求解观测数据的对数似然函数 L ( θ ) = log ⁡ P ( Y ∣ θ )

综合例题-求最小函数依赖集、确定候选键、判断最高满足的范式、模式分解

一、综合例题:          二、分析: 1、在函数依赖的范畴内,要掌握Armstrong公理的推理规则 2、利用推理规则计算属性集闭包和函数依赖集闭包 3、从而寻找与给定的函数依赖集等价的最小函数依赖集 4、在最小函数依赖集的基础上,确定候选键、判断范式级别 5、并利用分解算法来实现关系模式的规范化 6、从而消除不好的关系模式存在的数据冗余、更新异常和数据不

[HDU 5029] Relief grain (树链剖分+线段树)

HDU - 5029 其实这道题最大的难点不是树链剖分,而是怎么维护某个点被那些颜色染过,染过多少次 如果在线段树维护的话,很难做到,估计得树套树,而且空间会炸 好在这题是离线的,可以使用差分的思想来维护 对一段区间[l,r]染色 c,相当于在这段区间左端点 l打上 c标志,右端点 r+1打上 -c标志 然后扫一遍整个区间 (依照 dfs序扫一遍整棵树),期间不断维护一颗线段树 线段树

HDU 4791二分+线段树

13长沙现场赛水题 二分+线段树 二分出页数的所在位置 线段树查找后面区间最小值 #include "stdio.h"#include "string.h"#include "math.h"#include "stdlib.h"struct comp{int l,r,mid;__int64 min;} data[400100];__int64 a[100100],b[1

HDU 4578 线段树 多lazy操作

2013ACM-ICPC杭州赛区全国邀请赛 线段树裸题 bzoj 1798 升级版 add记录 加 mul记录 乘 sum[1]  记录一次方和 sum[2]  记录二次方和 sum[3]  记录三次方和 公式推导出: sum[3]=(sum[3]+(sqr3(m)*len())%mod + (3*m*sum[2])%mod + (3*sqr2(m)*sum[1])%mod) %m

HDU 4553 线段树双关键字区间合并

用两个关键字记录,分别为屌丝时间和女神时间 若屌丝约,更新屌丝时间 若女神约,更新屌丝和女神时间 学习,则两个全部清空 #include "stdio.h"#include "string.h"struct Data{int l,r,x1,x2,l1,l2,r1,r2;}data[400010];int Max(int a,int b){if (a<b) return b

HDU 3974 线段树(将树映射到区间)

第一次写将树映射到区间的线段树。。。 线段树部分很简单 主要是将原有的关系树根据BOSS关系从新编号 以便把每个BOSS所带领的员工全部压入一个连续区间内 然后记录每个BOSS的起始编号和他的最后一名的员工的编号 然后用线段树成端更新,单点查找即可 #include "stdio.h"#include "string.h"struct node{int l,r,val,l

HDU 5372 线段树

给出两种操作: 第i个0:在x位置插入一个长度为i的线段,并输出该线段共覆盖了多少之前加入的线段 1:删除第i次插入的线段 官方题解:对于新插入的线段,查询有多少个线段左端点大于等于该线段的左端点。 再查询有多少个线段的右端点大于该线段右端点, 两者之差就是答案。用两个树状数组搞定。时间复杂度nlog 思路很好理解,直接用一个线段树记录区间的左端点和右端点即可 #include