Chapter 3. 数据结构 线段树

Chapter 3. 数据结构 线段树

Sylvia‘s I.单点修改,区间查询.

模板:

//单点修改 区间求和 //1操作 单点修改//2操作 区间求和
#include<cstdio>
#include<iostream>
using namespace std;
#define MAXN 500005

int sum[MAXN<<2];
int n,m;
 void PushUp(int rt){//求和
     sum[rt]=sum[rt<<1]+sum[rt<<1|1];
 }
 void build(int l,int r,int rt){//建树
     if (l==r){
         scanf("%d",&sum[rt]);
         return;
     }
     int m=l+r>>1;
     build(l,m,rt<<1);
     build(m+1,r,rt<<1|1);
     PushUp(rt);
 }

void Update(int P,int Add,int l,int r,int rt){//单点修改
     if (l==r){
         sum[rt]+=Add;
         return;
     }
     int m=l+r>>1;
     if (P<=m) Update(P,Add,l,m,rt<<1);
      else Update(P,Add,m+1,r,rt<<1|1);
      PushUp(rt);
 }

 int Query(int L,int R,int l,int r,int rt){//区间查询
     if (L<=l&&r<=R) {
         return sum[rt];
     }
     int m=l+r>>1;
     int ret=0;
     if (L<=m) ret+=Query(L,R,l,m,rt<<1);
     if (m<R) ret+=Query(L,R,m+1,r,rt<<1|1);
     return ret;
 }
 int step,x,k;
 int main (){
     scanf("%d%d",&n,&m);
    build(1,n,1);
    for(int i=1;i<=m;i++){
        scanf("%d%d%d",&step,&x,&k);
        if (step==1) Update(x,k,1,n,1);
        if (step==2) printf("%d\n",Query(x,k,1,n,1));
    }
    return 0;
 }

Sylvia‘s II. 区间修改,区间查询.

模板:参考自:bogo的线段树模板

//1操作 乘法操作
//2操作 加法操作
//3操作 询问区间和
//最后的结果对P取模
#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
using namespace std;
#define MAX 100003
#define LL long long
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1

struct Segment_Tree{
    LL sum[MAX<<2];//数组开四倍
    LL lazy1[MAX<<2],lazy2[MAX<<2];//分别为乘法标记和加法标记
    LL P;//取模

    void PushUp(LL rt){//更新,对左右儿子求和
      sum[rt]=sum[rt<<1]+sum[rt<<1|1];
      sum[rt]%=P;//别忘记取模
    }

    void build(LL l,LL r,LL rt){//建树
        lazy1[rt]=1;//乘法标记记为1
        lazy2[rt]=0;//加法标记记为0
        if (l==r){
           scanf("%lld",&sum[rt]);
             return;
        }
        LL m=l+r>>1;
        build(lson);//左子树
        build(rson);//右子树
        PushUp(rt);//及时更新

    }

    void multiply (LL rt,LL x){//单乘
        lazy2[rt]*=x;//(A[i]+lazy2[i])*lazy1[i]=A[i]*lazy1[i]+lazy2[i]*lazy1[i],所以加法标记要乘x
        lazy2[rt]%=P;
        lazy1[rt]*=x;//乘法标记当然也要乘……
        lazy1[rt]%=P;
        sum[rt]*=x;//求和数组更要乘……
        sum[rt]%=P;
    }

    void Add (LL rt,LL x,LL len){//单加 ,len为区间长度
        lazy2[rt]=(lazy2[rt]+x)%P;//加法标记加
        sum[rt]=(sum[rt]+(x*len)%P)%P;//求和数组加
    }

    void PushDown (LL l,LL r,LL rt){
        if (lazy1[rt]!=1){//如果有乘法标记
            multiply(rt<<1,lazy1[rt]);//把标记下放到左儿子
            multiply(rt<<1|1,lazy1[rt]);//把标记下放到右儿子
            lazy1[rt]=1;//别忘记清除标记……
        }
        if (lazy2[rt]){//如果有加法标记……
            Add(rt<<1,lazy2[rt],(r+l>>1)-l+1);
            Add(rt<<1|1,lazy2[rt],r-(r+l>>1));
            lazy2[rt]=0;
        }
    } 

    void Addall(LL L,LL R,LL x,LL l,LL r,LL rt){//区间加
        if (L<=l&&r<=R){//当前区间包含在需要查询的区间中
            Add(rt,x,r-l+1);
            return;
        }
        LL m=l+r>>1;
        PushDown(l,r,rt);
        if (L<=m) Addall(L,R,x,lson);//如果需查询区间与左儿子有交集
        if (m<R) Addall(L,R,x,rson);//如果需查询区间与右儿子有交集
        PushUp(rt);//及时更新
    }

    void Mullall(LL L,LL R,LL x,LL l,LL r,LL rt){// 区间乘 ,与上面类似……
        if (L<=l&&r<=R){
            multiply(rt,x);
            return;
        }
        LL m=l+r>>1;
        PushDown(l,r,rt);//不要忘记查询左右儿子之前先把标记下放,血的教训……
        if (L<=m) Mullall(L,R,x,lson);
        if (m<R) Mullall(L,R,x,rson);
        PushUp(rt);
    }

    LL Query(LL L,LL R,LL l,LL r,LL rt){//区间查询
        if (L<=l&&r<=R){
            return sum[rt];
        }
        LL ret=0;
        LL m=l+r>>1;
        PushDown(l,r,rt);// 不要忘记下放标记
        if (L<=m) ret=(ret+Query(L,R,lson))%P;
        if (m<R) ret=(ret+Query(L,R,rson))%P;
        return ret;
    }

}Seg;
int main (){
    LL n,m,step,x,y,z;
    scanf("%lld%lld%lld",&n,&m,&Seg.P);
    Seg.build(1,n,1);//
    for (int i=1;i<=m;i++){
        scanf("%d",&step);
        switch(step){
            case 1:
                scanf("%lld%lld%lld",&x,&y,&z);
                Seg.Mullall(x,y,z,1,n,1);
                break;
            case 2:
                scanf("%lld%lld%lld",&x,&y,&z);
                Seg.Addall(x,y,z,1,n,1);
                break;
            case 3:
                scanf("%lld%lld",&x,&y);
                printf("%lld\n",Seg.Query(x,y,1,n,1));
                break;
        }
    }
    return 0;
}


活着

余华

老人和牛渐渐远去,我听到老人粗哑的令人感动的嗓音从远处传来,

他的歌声在空旷的傍晚像风一样飘扬,

老人唱道:

少年去游荡,中年想掘藏,老年做和尚。

炊烟在农舍的屋顶袅袅升起,在霞光四射的空中分散后消隐了。

女人吆喝孩子的声音此起彼伏,一个男人挑着粪桶从我跟前走过,扁担吱呀吱呀一路响了过去。

慢慢地,田野趋向了宁静,四周出现了模糊,霞光逐渐退去。

我知道黄昏正在转瞬即逝,黑夜从天而降了。

我看到广阔的土地袒露着结实的胸膛,

那是召唤的姿态,

就像,

女人召唤着她们的儿女,土地召唤着黑夜来临。



Sylvia

二零一七年五月十七日

时间: 2024-08-02 23:26:41

Chapter 3. 数据结构 线段树的相关文章

HDU 4902 Nice boat(数据结构-线段树)

Nice boat Problem Description There is an old country and the king fell in love with a devil. The devil always asks the king to do some crazy things. Although the king used to be wise and beloved by his people. Now he is just like a boy in love and c

HDU 1394 Minimum Inversion Number (数据结构-线段树)

Minimum Inversion Number Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 9514    Accepted Submission(s): 5860 Problem Description The inversion number of a given number sequence a1, a2, ..., an

不可能数据结构(线段树+思维+找规律)

不可能数据结构 Description造一道数据结构题是一件很累的事情.即使是有了坚固的数据结构造题程式,出题人仍然不能够一劳永逸.问题大概出在,造题程式并不总能生成对的题.比如,造题程式无意产生了一道出题人认为不可做题,这便成了一道错题.嘛,出了错题,其实也是没有关系的.出题人可以拿着暴力程序跑一个星期来跑出所需要的测试数据.一个星期跑不出来就延期一个星期机考好了,至于算法和讲题嘛,也就看看现场ac代码现学现卖咯?可能对你们来说很不幸的现实是,这道题似乎是个错题.给你一个初始n个元素的序列,有

数据结构——线段树

线段树是一种基于分治思想的类似于二叉树的数据结构,一般用于数组的信息统计,相比于树状数组,线段树有着更广阔的应用空间,但是相对的其代码量长,且常数大 一. 首先我们来讲线段树的建树过程,请看下图: 这张图就是线段树的存储结构,我们从最长的区间开始依次分成两部分,每一部分都有一个需要维护的权,建树过程比较简单,代码如下: inline void build(int l,int r,int rt) //l表示当前的左端点,r表示右端点,rt是当前区间的编号 { if(l == r) //当左右端点相

[数据结构-线段树] poj 2528

在一面墙上贴海报,贴的顺序给出了,求最后能被看到的海报数量. 纯粹的线段树模拟题. 但数据范围给了10^7,超内存了. 实际上这里用了一个小技巧,虽然墙的宽度是很大的,但海报数量只有10000,所以这10^7个数中真正用到的数很少,这样的话就只需要把没用到的数给"删去",剩下来的数从小到大映射为新的数,这样空间复杂度就大大降低了. 比如题目给的样例: 1 4 2 6 8 10 3 4 7 10   用到的数有:1 2 3 4 6 7 8 10 可以把它们映射为: 1 2 3 4 6 7

数据结构---线段树

线段树 转载请注明出处,谢谢!http://blog.csdn.net/metalseed/article/details/8039326  持续更新中···   一:线段树基本概念 1:概述 线段树,类似区间树,是一个完全二叉树,它在各个节点保存一条线段(数组中的一段子数组),主要用于高效解决连续区间的动态查询问题,由于二叉结构的特性,它基本能保持每个操作的复杂度为O(lgN)! 性质:父亲的区间是[a,b],(c=(a+b)/2)左儿子的区间是[a,c],右儿子的区间是[c+1,b],线段树

模板 - 数据结构 - 线段树(单点修改)

这里是以区间最大值为例,要修改成其他的运算,注意修改每个函数的运算以及query中返回的无关值. 这里的区间最大值设置的最小元素为-1(在query中表示与当前区间不相交的区间的结果). 注意因为调用的方式传入l与r是(1,n),所以这个线段树(包括a)其实是从1开始计数的. 最后,小心爆MAXM. const int MAXM=200000; int a[MAXM+5],st[(MAXM<<2)+5]; void build(int o,int l,int r){ if(l==r) st[o

数据结构——线段树(C++)

源代码: #include<cstdio>int m,n,num(0),h[100001];struct treetype{ int left,right,lefts,rights,sum; //本代码中,应用为半开半闭区间.}i[200020]; //应用了完全二叉树的结点个数公式.void x1(int t1,int t2) //建树.{ int t=++num; //注意,在线段树中,始终遵循左小右大原则. i[t].left=t1; i[t].right=t2; if (t1!=t2-

高级数据结构-线段树

1.模板(以维护最小值为例) #include<iostream> #include<stdio.h> #define LEN 11 #define MAX 1<<30 using namespace std; int arr[LEN]={1,3,7,6,8,5,3,2,7,2,9}; int st[LEN*3]; //segment tree int n; void init(int len){ n=1; while(n<len) n*=2; //不断乘以2,知