线段树- 算法训练 操作格子

问题描述

有n个格子,从左到右放成一排,编号为1-n。

共有m次操作,有3种操作类型:

1.修改一个格子的权值,

2.求连续一段格子权值和,

3.求连续一段格子的最大值。

对于每个2、3操作输出你所求出的结果。

输入格式

第一行2个整数n,m。

接下来一行n个整数表示n个格子的初始权值。

接下来m行,每行3个整数p,x,y,p表示操作类型,p=1时表示修改格子x的权值为y,p=2时表示求区间[x,y]内格子权值和,p=3时表示求区间[x,y]内格子最大的权值。

输出格式

有若干行,行数等于p=2或3的操作总数。

每行1个整数,对应了每个p=2或3操作的结果。

样例输入

4 3
1 2 3 4
2 1 3
1 4 3
3 1 4

样例输出

6
3

数据规模与约定

对于20%的数据n <= 100,m <= 200。

对于50%的数据n <= 5000,m <= 5000。

对于100%的数据1 <= n <= 100000,m <= 100000,0 <= 格子权值 <= 10000。

#include <string.h>
#include<iostream>
#include<vector>
#include<queue>
#include <algorithm>
#define INF 1<<30
using namespace std;
struct node{
    int r,l,Max,sum;
}tree[100000*4];//线段树需要四倍的结点空间

int Maxone(int a,int b){
    return a>=b?a:b;
}

void build(int l,int r,int k){//建树
    tree[k].l=l;
    tree[k].r=r;
    if(l==r){//左右端点相等为叶子结点
        int s;
        cin>>s;
        tree[k].Max=s;
        tree[k].sum=s;
        return;
    }
    int m=(r+l)/2;//m是区间的中间点
    build(l,m,2*k);//递归建立左右子树
    build(m+1,r,2*k+1);//
    tree[k].sum=tree[k*2].sum+tree[k*2+1].sum;//更新结点区间的和为左右子树结点的和
    tree[k].Max=Maxone(tree[k*2].Max,tree[k*2+1].Max);//更新结点区间的最大值为左右子树最大值中较大的那个
}

void fix(int k,int x,int y){//修改结点的值
    if(tree[k].l==tree[k].r){//当左右端点相等,则找到对应叶子结点
        tree[k].sum=y;
        tree[k].Max=y;
        return;
    }
    int m=(tree[k].r+tree[k].l)/2;
    if(x<=m) fix(2*k,x,y);
    else fix(2*k+1,x,y);
    tree[k].sum=tree[k*2].sum+tree[k*2+1].sum;//更新结点区间的和为左右子树结点的和
    tree[k].Max=Maxone(tree[k*2].Max,tree[k*2+1].Max);//更新结点区间的最大值为左右子树最大值中较大的那个
}

int sum(int k,int x,int y){//求和
    int ans=0;
    if(tree[k].l>=x&&tree[k].r<=y){//当结点区间都在要求区间之内,则将区间的和加上,结束递归
        ans=tree[k].sum;
        return ans;
    }
    int m=(tree[k].r+tree[k].l)/2;
    if(x<=m) ans+=sum(2*k,x,y);//当结点区间不都是在所要求得区间内,如果x<=m,则说明要求的区间存在左孩子中,则ans加上左孩子的那部分
    if(y>m) ans+=sum(2*k+1,x,y);//如果y>m,则说明要求的区间存在右孩子中,则ans加上右孩子的那部分
    return ans;
}

int findmax(int k,int x,int y){//求最大值
     int mmax=0;
     if(tree[k].l>=x&&tree[k].r<=y){
        mmax=tree[k].Max;
        return mmax;
    }
    int m=(tree[k].r+tree[k].l)/2;
    if(x<=m) mmax=findmax(2*k,x,y);
    if(y>m) {
        int s=findmax(2*k+1,x,y);
        mmax=s>mmax?s:mmax;
    }
    return mmax;
}

int main()
{
    int n,m;
    cin>>n>>m;
    build(1,n,1);
    int p,x,y;
    for(int i=0;i<m;i++){
        cin>>p>>x>>y;
        if(p==1) fix(1,x,y);
        else if(p==2){
            cout<<sum(1,x,y)<<endl;
        }
        else if(p==3) {
            cout<<findmax(1,x,y)<<endl;
        }
    }
    return 0;
}

原文地址:https://www.cnblogs.com/zdl2234/p/10381464.html

时间: 2024-08-02 05:46:13

线段树- 算法训练 操作格子的相关文章

蓝桥杯 算法训练 操作格子 [ 线段树 ]

传送门 算法训练 操作格子 时间限制:1.0s   内存限制:256.0MB 锦囊1 锦囊2 锦囊3 问题描述 有n个格子,从左到右放成一排,编号为1-n. 共有m次操作,有3种操作类型: 1.修改一个格子的权值, 2.求连续一段格子权值和, 3.求连续一段格子的最大值. 对于每个2.3操作输出你所求出的结果. 输入格式 第一行2个整数n,m. 接下来一行n个整数表示n个格子的初始权值. 接下来m行,每行3个整数p,x,y,p表示操作类型,p=1时表示修改格子x的权值为y,p=2时表示求区间[x

算法训练 操作格子

算法训练 操作格子 时间限制:1.0s   内存限制:256.0MB 问题描述 有n个格子,从左到右放成一排,编号为1-n. 共有m次操作,有3种操作类型: 1.修改一个格子的权值, 2.求连续一段格子权值和, 3.求连续一段格子的最大值. 对于每个2.3操作输出你所求出的结果. 输入格式 第一行2个整数n,m. 接下来一行n个整数表示n个格子的初始权值. 接下来m行,每行3个整数p,x,y,p表示操作类型,p=1时表示修改格子x的权值为y,p=2时表示求区间[x,y]内格子权值和,p=3时表示

算法训练 操作格子 线段树板子题

问题描述 有n个格子,从左到右放成一排,编号为1-n. 共有m次操作,有3种操作类型: 1.修改一个格子的权值, 2.求连续一段格子权值和, 3.求连续一段格子的最大值. 对于每个2.3操作输出你所求出的结果. 输入格式 第一行2个整数n,m. 接下来一行n个整数表示n个格子的初始权值. 接下来m行,每行3个整数p,x,y,p表示操作类型,p=1时表示修改格子x的权值为y,p=2时表示求区间[x,y]内格子权值和,p=3时表示求区间[x,y]内格子最大的权值. 输出格式 有若干行,行数等于p=2

线段树区间更新操作及Lazy思想(详解)

此题题意很好懂:  给你N个数,Q个操作,操作有两种,‘Q a b ’是询问a~b这段数的和,‘C a b c’是把a~b这段数都加上c. 需要用到线段树的,update:成段增减,query:区间求和 介绍Lazy思想:lazy-tag思想,记录每一个线段树节点的变化值,当这部分线段的一致性被破坏我们就将这个变化值传递给子区间,大大增加了线段树的效率. 在此通俗的解释我理解的Lazy意思,比如现在需要对[a,b]区间值进行加c操作,那么就从根节点[1,n]开始调用update函数进行操作,如果

poj 3667 Hotel (线段树的合并操作)

Hotel The cows are journeying north to Thunder Bay in Canada to gain cultural enrichment and enjoy a vacation on the sunny shores of Lake Superior. Bessie, ever the competent travel agent, has named the Bullmoose Hotel on famed Cumberland Street as t

1.1 线段树的基础操作

本篇对应的是luogu的线段树1 概况: 如下图就是一棵线段树,线段树上的每一个点记录的都是一个区间,所以线段树支持对于区间和点的动态操作,可以在线查询和更改区间上的最值,求和等 时间复杂度:O(n) 使用线段树的情况: 满足区间加法:已知左右两子树的全部信息,一定能够推出父节点 线段树维护的内容根据题目的要求而定 线段树的分类: 根据题目中对于查询和修改区间的不同要求,大致将线段树分为三类: problem1:单点修改,单点查询 problem2:区间修改,单点查询 problem3:区间修改

UVa 11992 Fast Matrix Operations(线段树双懒操作,二维转一维)

题意:给个r*c的矩形,三种操作,将一个子矩形权值+v,将一个子矩阵权值赋值为v,查询一个子矩阵sum,max,min.起初矩阵权值为0,保证r<=20,r*c<=1e6,操作次数不超过10000 链接: http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=18697 题解:将二维转一维,设当前点为(x,y),则它在线段树上的点为(x-1)*c+y,由于行不超过20行,不妨枚举每一行,去操作.对于一维的线段树,要进行赋值,加法

CodeForces 620E New Year Tree(线段树的骚操作第二弹)

The New Year holidays are over, but Resha doesn't want to throw away the New Year tree. He invited his best friends Kerim and Gural to help him to redecorate the New Year tree. The New Year tree is an undirected tree with n vertices and root in the v

线段树的复杂操作

一,线段树做区间乘法 首先要明白,乘法操作高于加法操作 一般的话会开long long ,要去模 对于一个节点o,我们设区间和为sum[o],加法标记为add[o],乘法标记为mul[o] mul标记的初始值是1,add标记初始值是0 在修改值的时候,add的维护需要累加,mul的维护需要累乘 此时当我们进行区间加的时候,一切照旧 但是当进行区间乘法的时候 儿子节点的add标记分别先乘上父亲节点的乘标记再加上父亲节点的加标记 (因为父亲节点的加标记已经乘上了一些乘标记了,不需要再乘一次) 儿子节