线段树(白书版)

个人感觉有点坑

add用的标记永久化

set用的标记下传

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#define lson o<<1,l,m
#define rson (o<<1)+1,m+1,r
#define ls o<<1
#define rs (o<<1)+1
using namespace std;
const int N=1e4,INF=1e9;

int minv[N<<2],maxv[N<<2],sumv[N<<2];
int addv[N<<2],setv[N<<2];
int ql,qr,p,v;

//point
int queryMin(int o,int l,int r){
    int m=l+((r-l)>>1),ans=INF;
    if(ql<=l&&r<=qr) return minv[o];
    if(ql<=m) ans=min(ans,queryMin(lson));
    if(m+1<=qr) ans=min(ans,queryMin(rson));
    return ans;
}

void change(int o,int l,int r){
    int m=l+((r-l)>>1);
    if(l==r)
        minv[o]=v;
    else{
        if(p<=m) change(lson);
        else change(rson);
        minv[o]=min(minv[ls],minv[rs]);
    }
}

//add
int _min,_max,_sum;
void maintain(int o,int l,int r){
    sumv[o]=minv[o]=maxv[o]=0;//!!
    if(l<r){
        sumv[o]=sumv[ls]+sumv[rs];
        minv[o]=min(minv[ls],minv[rs]);
        maxv[o]=max(maxv[ls],maxv[rs]);
    }
    minv[o]+=addv[o];
    maxv[o]+=addv[o];
    sumv[o]+=addv[o]*(r-l+1);
}

void addRange(int o,int l,int r){
    if(ql<=l&&r<=qr)
        addv[o]+=v;
    else{
        int m=(r+l)>>1;
        if(ql<=m) addRange(lson);
        if(m+1<=qr) addRange(rson);
    }
    maintain(o,l,r);
}

void queryRange(int o,int l,int r,int add){
    if(ql<=l&&r<=qr){
        _sum+=sumv[o]+add*(r-l+1);
        _min=min(_min,minv[o]+add);
        _max=max(_max,maxv[o]+add);
    }else{
        int m=(r+l)>>1;
        if(ql<=m) queryRange(lson,add+addv[o]);
        if(m+1<=qr) queryRange(rson,add+addv[o]);
    }
}

//set
void mt(int o,int l,int r){
    if(setv[o]>=0){
        sumv[o]=setv[o]*(r-l+1);
        minv[o]=maxv[o]=setv[o];
    }else if(l<r){
        sumv[o]=sumv[ls]+sumv[rs];
        minv[o]=min(minv[ls],minv[rs]);
        maxv[o]=max(maxv[ls],maxv[rs]);
    }
}

void pushDown(int o){
    if(setv[o]>=0){
        setv[ls]=setv[rs]=setv[o];
        setv[o]=-1;
    }
}

void setRange(int o,int l,int r){
    if(ql<=l&&r<=qr) setv[o]=v;
    else {
        pushDown(o);
        int m=(r+l)>>1;
        if(ql<=m) setRange(lson); else mt(lson);
        if(m+1<=qr) setRange(rson); else mt(rson);
    }
    mt(o,l,r);
}

void queryRange(int o,int l,int r){
    if(setv[o]>=0){
        _sum+=setv[o]*(r-l+1);
        _min=min(_min,setv[o]);
        _max=max(_max,setv[o]);
    }else if(ql<=l&&r<=qr){
        _sum+=sumv[o];
        _min=min(_min,minv[o]);
        _max=max(_max,maxv[o]);
    }else{
        int m=(r+l)>>1;
        if(ql<=m) queryRange(lson);
        if(m+1<=qr) queryRange(rson);
    }
}

int n,t;
int main(int argc, const char * argv[]) {

//freopen("in.txt","r",stdin);
//freopen("2.txt","w",stdout);

    cin>>n>>t;
    for(int i=1;i<=n;i++){
        cin>>v;
        ql=qr=i;
        addRange(1,1,n);
    }

//    for(int i=1;i<=n<<1;i++) printf("%d %d %d %d\n",i,sumv[i],minv[i],maxv[i]);

    while(t--){
        int flag;
        cin>>flag;
        if(flag==1){
            cin>>ql>>qr;
            _sum=0;_min=INF;_max=-INF;
            queryRange(1,1,n,0);
            printf("%d\n",_sum);
        }else{
            cin>>ql>>qr>>v;
            addRange(1,1,n);
            _sum=0;_min=INF;_max=-INF;
            queryRange(1,1,n,0);
            printf("%d\n",_sum);
        }
    }

    return 0;
}
时间: 2024-07-30 20:32:23

线段树(白书版)的相关文章

ACM——I Hate It(线段树的进化版)

Description 很多学校流行一种比较的习惯.老师们很喜欢询问,从某某到某某当中,分数最高的是多少. 这让很多学生很反感. 不管你喜不喜欢,现在需要你做的是,就是按照老师的要求,写一个程序,模拟老师的询问.当然,老师有时候需要更新某位同学的成绩. Input 本题目包含多组测试,请处理到文件结束. 在每个测试的第一行,有两个正整数 N 和 M ( 0<N<=200000,0<M<5000 ),分别代表学生的数目和操作的数目. 学生ID编号分别从1编到N. 第二行包含N个整数,

线段树 &amp; 题目

首先说下我写的线段树吧. 我是按照线段树[完全版]那个人的写法来写的,因为网上大多数题解都是按照他的写法来写. 确实比较飘逸,所以就借用了. 节点大小是maxn是4倍,准确来说是大于maxn的2^x次方的最小值的两倍.因为要pushUp的时候会去到一些无用的节点. 就是在叶子节点的时候还pushUp了的话,要去到两个无用的节点,所以是最小值的两倍. lson 和 rson 用宏定义写了.因为是固定的量. 线段树不必保存自身的区间,因为一边传递过去的时候,在函数里就有区间表示,无谓开多些无用的变量

UVALive - 3938 分治,线段树,求动态最大连续和

UVALive - 3938 题意: 给出一个长度为n的整数序列D,你的任务是对m个询问作出回答.对于询问(a,b),需要找到两个下标x和y,使得a≤x≤y≤b,并且Dx+Dx+1+...+Dy尽量大.如果有多组满足条件的x和y,x应该尽量小.如果还有多解,y应该尽量小. tags: 分治思想,线段树       大白书201 区别于静态最大连续和,只能用分治算法: 最优解要么完全在左半序列,要么完全在右半序列,要么跨越中点. 我们构造一棵线段树,维护 3 个值:最大连续和 max_sub ,最

指针版线段树

只是作一下,以后必须得写数组版的...???(然而很好写? 哦对,唯一的好处就是内存少一点,没了.(coding量似乎并不会少很多?也不会多很多?雾) 还有很重要的一点就是慢...(尽管虽然没有慢多少?该卡还是卡?) 哎呀真是好纠结... 问了些神犇,似乎大家并不知道线段树还能用数组写... 呵呵... 然后看了一眼内存,指针严格开2n-1就好,而数组其实是要开4n的.... COJ上的数据太水了,数据只有大概... 所以呢.....要不我先用指针写几次再说? 不过是真心写着舒服. 1 #inc

求逆序对(线段树版)

一个序列a1,a2,a3...aN,求出满足:ai > aj 且 i < j 的个数. 一个最容易想到的方法就是枚举所有的i,j看看是否满足,显然是O(n^2)的复杂度.不够好. 可以这样考虑,开一个数组保存这n个数出现的位置和对应的次数,这个数组要开到a数组里最大的那个数MAX,也就是hash,初始状态数组里没有元素,每个数对应的个数都是0. 如果考虑第i个数,找到比它大的所有的数 的个数,查找的范围即 ai+1~MAX,这就是到i这个位置的逆序对的总和,接着把a[i]这个数添加到数组里,也

白书 5.4.3 果园的里的树

果园里的树排列成矩阵.他们的x和y的坐标均是1~99的整数.输入若干个三角形,依次统计每个三角形内部和边界上共有多少棵树. 输入: 1.5  1.5       1.5  6.8      6.8  1.5 10.7  6.9     8.5  1.5      14.5  1.5 此题用三角形有向面积来解,求有向面积2倍的函数为: double area(double x0,double y0,double x1,double y1,double x2,double,y2) { return

[XJOI NOI2015模拟题13] C 白黑树 【线段树合并】

题目链接:XJOI - NOI2015-13 - C 题目分析 使用神奇的线段树合并在 O(nlogn) 的时间复杂度内解决这道题目. 对树上的每个点都建立一棵线段树,key是时间(即第几次操作),动态开点. 线段树的节点维护两个值,一个是这段时间内的 1 操作个数,另一个是这段时间内变化的黑色节点权值和. 在处理所有操作的时候,每棵线段树都是仅代表树上的一个点,因此线段树的每个节点维护的就是这段时间内以这个点为 a 的 1 操作个数和这段时间内这个点的黑色节点权值和(这个点 x 由黑变白就 -

非结构体线段树版 ZJU 1610 Count the Colors (线段树区间更新)

Painting some colored segments on a line, some previously painted segments may be covered by some the subsequent ones. Your task is counting the segments of different colors you can see at last. Input The first line of each data set contains exactly

SegmentTree-Complete 线段树完全版

线段树完全版关键词:延迟加载.懒标记Lazy Tag 单点更新的情况比较简单.请看 线段树基础版 下面说说区间更新的情况. 场景是这样的,还是刚刚的数,求区间的和. #define lson rt<<1 #define rson rt<<1|1 #define len (r-l+1) //(l,r)区间的长度 这次是区间更新,我们要用到区间的长度 建树 build和pushUp不变.我们把树建立好打印一下: [1]:36 [2]:26 [3]:10 [4]:15 [5]:11 [6