zoukankan      html  css  js  c++  java
  • P3372 【模板】线段树 1

    P3372 【模板】线段树 1

    这个题目相对我们平时做的简单的线段树的题目(区间加,求最值)而言,这里是(区间加,求和),所以父亲节点延迟标记下传的时候父亲节点的值的增量要乘上孩子数,因为求最值的情况,整个区间同加最值不会改变。

    t[id].s += (t[id].right - t[id].left + 1) * t[id].lazy;//更新节点的值 

    题目描述

    如题,已知一个数列,你需要进行下面两种操作:

    1.将某区间每一个数加上x

    2.求出某区间每一个数的和

    输入输出格式

    输入格式:

    第一行包含两个整数N、M,分别表示该数列数字的个数和操作的总个数。

    第二行包含N个用空格分隔的整数,其中第i个数字表示数列第i项的初始值。

    接下来M行每行包含3或4个整数,表示一个操作,具体如下:

    操作1: 格式:1 x y k 含义:将区间[x,y]内每个数加上k

    操作2: 格式:2 x y 含义:输出区间[x,y]内每个数的和

    输出格式:

    输出包含若干行整数,即为所有操作2的结果。

    输入输出样例

    输入样例#1: 复制
    5 5
    1 5 4 2 3
    2 2 4
    1 2 3 2
    2 3 4
    1 1 5 1
    2 1 4
    输出样例#1: 复制
    11
    8
    20

    说明

    时空限制:1000ms,128M

    数据规模:

    对于30%的数据:N<=8,M<=10

    对于70%的数据:N<=1000,M<=10000

    对于100%的数据:N<=100000,M<=100000

    (数据已经过加强^_^,保证在int64/long long数据范围内)

    样例说明:

    这个题目线段树的模板题,因为这里是求和,所以每个节点里面记录的是所包含的孩子的和。

    因为不是求最值,所以在传递延迟标记的时候延迟标记里面的值和节点记录的孩子的和不一样。

    因为我们从每个节点里面可以知道孩子数目,所以segTree[root].sum+=(segTree[root].r-segTree[root].l+1)孩子数目 *segTree[root].addMark;

     1 #include <bits/stdc++.h>
     2 #define ll long long
     3 using namespace std;
     4 ll n, m;
     5 ll L = 1, a[100100];
     6 struct st_n//SegmentTree node 线段树节点 
     7 {
     8     ll left, right;//每个节点的控制范围 
     9     ll lazy, s;       //s表示和 
    10 }t[300005];
    11 void build_tree()//建线段树 
    12 {
    13     while(L < n) L *= 2;
    14     for(ll i = 1; i <= n; i++) 
    15         t[i + L - 1].left = t[i + L - 1].right = i, 
    16         t[i + L - 1].s = a[i];//将最后一层赋值 
    17     for(ll i = L + n;i < 2 * L;i++)
    18         t[i].left = t[i].right = i - L + 1;
    19     for(ll i = L - 1; i >= 1; i--)
    20         t[i].left = t[2 * i].left,
    21         t[i].right = t[2 * i + 1].right,
    22         t[i].s += t[2 * i + 1].s + t[2 * i].s;
    23         //建树 
    24 }
    25 
    26 void push(ll id)//发放lazy值给左右子 
    27 {
    28     t[2 * id].lazy += t[id].lazy;//左子 
    29     t[2 * id + 1].lazy += t[id].lazy;//右子 
    30     t[id].s += (t[id].right - t[id].left + 1) * t[id].lazy;//更新节点的值 
    31     t[id].lazy = 0;
    32 }
    33 
    34 //区间修改 
    35 void change(ll id, ll left, ll right, ll d)
    36 {
    37     if(t[id].left == left && t[id].right == right)//找到了 
    38     {
    39         t[id].lazy += d;
    40         return ;
    41     }
    42     push(id);//发放lazy值 
    43     if(t[2 * id].right >= right) change(2 * id, left, right, d);//如果左子的右管辖范围比所求大,则只在左子中继续change 
    44     else if(t[2 * id + 1].left <= left) change(2 * id + 1, left, right, d);//同上 
    45     else
    46     {
    47         change(2 * id, left, t[2 * id].right, d);
    48         change(2 * id + 1, t[2 * id + 1].left, right, d);
    49         //左右子分别查找 
    50     }
    51     t[id].s = t[2 * id].s + t[2 * id + 1].s 
    52             + (t[2 * id].right - t[2 * id].left + 1) * t[2 * id].lazy
    53             + (t[2 * id + 1].right - t[2 * id + 1].left + 1) * t[2 * id + 1].lazy;
    54     //上面这步很重要,它的功能也是更新(其实我原本想写个函数) 
    55 }
    56 ll query(ll id, ll left, ll right)//查询 
    57 {
    58     //与change很像,就不多说了。 
    59     if(t[id].left == left && t[id].right == right)
    60         return t[id].s + (t[id].right - t[id].left + 1) * t[id].lazy;
    61     push(id); 
    62     if(t[2 * id].right >= right) return query(2 * id, left, right);
    63     else if(t[2 * id + 1].left <= left) return query(2 * id + 1, left, right);
    64     else return query(2 * id, left, t[2 * id].right) + query(2 * id + 1, t[2 * id + 1].left, right);
    65 }
    66 int main(int argc, char *argv[])
    67 {
    68     cin >> n >> m;
    69     for(ll i = 1; i <= n; i++) cin >> a[i];
    70     build_tree();//建树 
    71     for(ll i = 1;i <= m;i++)
    72     {
    73         ll p, x, y;
    74         ll d;
    75         cin >> p >> x >> y;
    76         if(p == 1)
    77         {
    78             cin >> d;
    79             change(1, x, y, d);
    80         }
    81         else
    82             cout << query(1, x, y) << endl;
    83     }
    84     return 0;
    85 }

    其它代码(不正确)

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 const int MAXN=1e6+10;
     4 int a[MAXN];
     5 struct node{
     6     int l,r;
     7     int sum; 
     8     int addMark;//延迟标记
     9 }segTree[4*MAXN];
    10 
    11 /*
    12 建树的时候做的事情:
    13 1、给左右边界,要求的值(这里是求和),以及传递标记赋初值
    14 2、更新每个点表示的要求的值(这里是求和)
    15 */ 
    16 void build(int root,int l,int r){
    17     segTree[root].l=l;
    18     segTree[root].r=r;
    19     segTree[root].sum=0;
    20     segTree[root].addMark=0;
    21     int mid=(l+r)/2;
    22     if(l==r) segTree[root].sum=a[l];
    23     else{
    24         build(root*2,l,mid);//建左子树
    25         build(root*2+1,mid+1,r);//建右子树
    26     }
    27     segTree[root].sum=segTree[root*2].sum+segTree[root*2+1].sum;
    28 }
    29 
    30 //区间更新,区间更新自然能包括单点更新 
    31 void update(int root,int l,int r,int add){
    32     if(segTree[root].r<l||segTree[root].l>r){//区间无关
    33         return;
    34     }
    35     if(segTree[root].l>=l&&segTree[root].r<=r){//区间包含 
    36         segTree[root].addMark=add;
    37     }else{//区间交叉 
    38         int mid=(segTree[root].l+segTree[root].r)/2;
    39         //在左边子树去更新 
    40         if(l<=mid) update(2*root,l,r,add);
    41         //在右边子树去更新 
    42         if(r>=mid) update(2*root+1,l,r,add); 
    43     }
    44     segTree[root].sum=segTree[root*2].sum+segTree[root*2+1].sum;
    45 }
    46 
    47 void print(int n){
    48     cout<<"segTree[i].l"<<"   "<<"segTree[i].r"<<"   "<<"segTree[i].sum"<<"   "<<"segTree[i].addMark"<<endl;
    49     for(int i=1;i<=n;i++){
    50         cout<<segTree[i].l<<"   "<<segTree[i].r<<"   "<<segTree[i].sum<<"   "<<segTree[i].addMark<<"   "<<endl;
    51     }
    52 }
    53 
    54 int main(){
    55     freopen("in.txt","r",stdin); 
    56     int n,m;
    57     cin>>n>>m;
    58     for(int i=1;i<=n;i++) cin>>a[i];
    59     build(1,1,n);
    60     print(4*n);
    61     for(int i=1;i<=m;i++){
    62         int op;cin>>op;
    63         if(op==1){
    64             int x,y,k;
    65             cin>>x>>y>>k;
    66             update(1,x,y,k);
    67         }else{
    68             int  x,y;
    69             cin>>x>>y;
    70 //            query(1,x,y);
    71         }
    72     }
    73     print(4*n);
    74     return 0;
    75 } 
  • 相关阅读:
    Windows下快速搭建安卓开发环境android-studio
    使用Android Studio搭建Android集成开发环境
    手动安装配置Android Studio
    android studio 各种问题 应该能帮助到你们
    如何清除XP的网络共享密码
    一个语言的“入流”,而是和这种语言进入某一子行业的契机有关
    必须冷静、必须听话,赶紧走
    QWidget继承自QPaintDevice,这样就可以直接把QWidget传入QPainter的构造函数,比如QPainter(mylabel),然后设置QWidget的长宽后直接进行作画了
    ActiveMQ
    开源word操作组件DocX的记录
  • 原文地址:https://www.cnblogs.com/Renyi-Fan/p/8134448.html
Copyright © 2011-2022 走看看